diff --git a/libraries/randomHelpers/library.json b/libraries/randomHelpers/library.json index 58a591ce..1e889c99 100644 --- a/libraries/randomHelpers/library.json +++ b/libraries/randomHelpers/library.json @@ -15,7 +15,7 @@ "type": "git", "url": "https://github.com/RobTillaart/randomHelpers.git" }, - "version": "0.2.3", + "version": "0.2.4", "license": "MIT", "frameworks": "arduino", "platforms": "*", diff --git a/libraries/randomHelpers/library.properties b/libraries/randomHelpers/library.properties index 3a9b6411..f6ec2995 100644 --- a/libraries/randomHelpers/library.properties +++ b/libraries/randomHelpers/library.properties @@ -1,5 +1,5 @@ name=randomHelpers -version=0.2.3 +version=0.2.4 author=Rob Tillaart maintainer=Rob Tillaart sentence=Arduino library with helper function for faster random bits diff --git a/libraries/randomHelpers/randomHelpers.cpp b/libraries/randomHelpers/randomHelpers.cpp new file mode 100644 index 00000000..c03311c7 --- /dev/null +++ b/libraries/randomHelpers/randomHelpers.cpp @@ -0,0 +1,238 @@ +#pragma once +// +// FILE: randomHelpers.cpp +// AUTHOR: Rob Tillaart +// VERSION: 0.2.4 +// PURPOSE: Arduino library with helper function for faster random bits +// URL: https://github.com/RobTillaart/randomHelpers +// +// HISTORY: +// 0.2.4 2022-04-15 fix #4 split .h in .h and .cpp +// 0.2.3 2021-12-27 update library.json, license, minor edits +// 0.2.2 2021-11-15 update Arduino-CI, readme.md badges +// add seedMarsaglia(uint32_t a, uint32_t b) +// fix randomDice() +// 0.2.1 2021-01-07 Arduino-CI +// 0.2.0 2020-07-01 rewrite. +// 0.1.01 2015-08-18 bug fixes and further optimizations. +// 0.1.00 2015-08-17 initial version. + + +#include "randomHelpers.h" + + +// the idea is to have one buffer ( __randomBuffer) which holds 32 random bits. +// Every call fetches bits from that buffer and if it does not hold enough +// bits any more it fills the buffer first. This way the relative expensive +// calls to random() which produces a 32 bit number are minimized in an +// efficient way. +// +// TBD: put it in a class ? + +uint32_t __randomBuffer = 0; +uint8_t __randomIdx = 0; + + +/////////////////////////////////////////////////////////////////////////// +// +// An example of a simple pseudo-random number generator is the +// Multiply-with-carry method invented by George Marsaglia. +// it has two initializers (not zero) which can be changed +// to seed the generator. +// +uint32_t m_w = 1; +uint32_t m_z = 2; + + +uint32_t Marsaglia() +{ + m_z = 36969L * (m_z & 65535L) + (m_z >> 16); + m_w = 18000L * (m_w & 65535L) + (m_w >> 16); + return (m_z << 16) + m_w; /* 32-bit result */ +} + + +bool seedMarsaglia(uint32_t a, uint32_t b) +{ + if (a == 0 || b == 0) return false; + m_w = a; + m_z = b; + return true; +} + + +uint32_t getRandom32() +{ + // return random(0xFFFFFFFF); // use the built in + return Marsaglia(); +} + + +bool getRandom1() +{ + if (__randomIdx < 1) + { + __randomBuffer = getRandom32(); + __randomIdx = 32; + } + bool rv = __randomBuffer & 0x01; + __randomBuffer >>= 1; + __randomIdx--; + return rv; +} + +// typical use +bool inline flipCoin() +{ + return getRandom1(); +}; + + +uint8_t getRandom4() +{ + if (__randomIdx < 4) + { + __randomBuffer = getRandom32(); + __randomIdx = 32; + } + uint8_t rv = __randomBuffer & 0x0F; + __randomBuffer >>= 4; + __randomIdx -= 4; + return rv; +} + + +uint8_t getRandom5() +{ + if (__randomIdx < 5) + { + __randomBuffer = getRandom32(); + __randomIdx = 32; + } + uint8_t rv = __randomBuffer & 0x1F; + __randomBuffer >>= 5; + __randomIdx -= 5; + return rv; +} + + +uint8_t getRandom6() +{ + if (__randomIdx < 6) + { + __randomBuffer = getRandom32(); + __randomIdx = 32; + } + uint8_t rv = __randomBuffer & 0x3F; + __randomBuffer >>= 6; + __randomIdx -= 6; + return rv; +} + + +// typical use +uint8_t throwDice() +{ + if (__randomIdx < 16) + { + __randomBuffer = getRandom32(); + __randomIdx = 32; + } + uint16_t rv = __randomBuffer % 6 + 1; + __randomBuffer >>= 3; + __randomIdx -= 3; + return rv; +} + + +uint8_t getRandom8() +{ + if (__randomIdx < 8) + { + __randomBuffer = getRandom32(); + __randomIdx = 32; + } + uint8_t rv = __randomBuffer & 0xFF; + __randomBuffer >>= 8; + __randomIdx -= 8; + return rv; +} + + +uint16_t getRandom16() +{ + if (__randomIdx < 16) + { + __randomBuffer = getRandom32(); + __randomIdx = 32; + } + uint16_t rv = __randomBuffer & 0xFFFF; + __randomBuffer >>= 16; + __randomIdx -= 16; + return rv; +} + + +uint32_t getRandom24() +{ + return getRandom32() & 0xFFFFFF; +} + + +uint64_t getRandom64() +{ + uint64_t rv = getRandom32(); + rv <<= 32; + rv |= getRandom32(); + return rv; +} + +/* +// works well for 1..16; but above it is worse +uint32_t getRandomBits(uint8_t n) +{ + if (__randomIdx < n) + { + __randomBuffer = getRandom32(); + __randomIdx = 32; + } + uint32_t rv = __randomBuffer & ((1UL << n) - 1); + __randomBuffer >>= n; + __randomIdx -= n; + return rv; +} +*/ + + +// n = 1..31 +// TODO: performance gain too low for n > 16 +uint32_t getRandomBits(uint8_t n) +{ + uint32_t rv = 0; + + // for large values of n the more straightforward approach is faster (UNO). + if (n > 32) n = 32; + if (n >= 20) return getRandom32() >> (32 - n); + + if (n >= __randomIdx) + { + if (__randomIdx > 0) + { + n -= __randomIdx; + rv = __randomBuffer << n; + } + __randomBuffer = getRandom32(); + __randomIdx = 32; + } + if (n > 0) // more bits needed? + { + rv |= __randomBuffer & ((1UL << n) - 1); + __randomBuffer >>= n; + __randomIdx -= n; + } + return rv; +} + + +// -- END OF FILE -- + diff --git a/libraries/randomHelpers/randomHelpers.h b/libraries/randomHelpers/randomHelpers.h index 773391f5..a913dac8 100644 --- a/libraries/randomHelpers/randomHelpers.h +++ b/libraries/randomHelpers/randomHelpers.h @@ -1,26 +1,15 @@ #pragma once // // FILE: randomHelpers.h -// AUTHOR: Rob dot Tillaart at gmail dot com -// VERSION: 0.2.3 +// AUTHOR: Rob Tillaart +// VERSION: 0.2.4 // PURPOSE: Arduino library with helper function for faster random bits // URL: https://github.com/RobTillaart/randomHelpers -// -// HISTORY: -// 0.2.3 2021-12-27 update library.json, license, minor edits -// 0.2.2 2021-11-15 update Arduino-CI, readme.md badges -// add seedMarsaglia(uint32_t a, uint32_t b) -// fix randomDice() -// 0.2.1 2021-01-07 Arduino-CI -// 0.2.0 2020-07-01 rewrite. -// 0.1.01 2015-08-18 bug fixes and further optimizations. -// 0.1.00 2015-08-17 initial version. - #include "Arduino.h" -#define RANDOM_HELPERS_VERSION (F("0.2.3")) +#define RANDOM_HELPERS_VERSION (F("0.2.4")) // the idea is to have one buffer ( __randomBuffer) which holds 32 random bits. @@ -31,9 +20,6 @@ // // TBD: put it in a class ? -uint32_t __randomBuffer = 0; -uint8_t __randomIdx = 0; - /////////////////////////////////////////////////////////////////////////// // @@ -42,152 +28,34 @@ uint8_t __randomIdx = 0; // it has two initializers (not zero) which can be changed // to seed the generator. // -uint32_t m_w = 1; -uint32_t m_z = 2; +uint32_t Marsaglia(); -uint32_t Marsaglia() -{ - m_z = 36969L * (m_z & 65535L) + (m_z >> 16); - m_w = 18000L * (m_w & 65535L) + (m_w >> 16); - return (m_z << 16) + m_w; /* 32-bit result */ -} +bool seedMarsaglia(uint32_t a, uint32_t b); +uint32_t getRandom32(); -bool seedMarsaglia(uint32_t a, uint32_t b) -{ - if (a == 0 || b == 0) return false; - m_w = a; - m_z = b; - return true; -} - - -uint32_t getRandom32() -{ - // return random(0xFFFFFFFF); // use the built in - return Marsaglia(); -} - - -bool getRandom1() -{ - if (__randomIdx < 1) - { - __randomBuffer = getRandom32(); - __randomIdx = 32; - } - bool rv = __randomBuffer & 0x01; - __randomBuffer >>= 1; - __randomIdx--; - return rv; -} +bool getRandom1(); // typical use -bool inline flipCoin() -{ - return getRandom1(); -}; +bool inline flipCoin(); +uint8_t getRandom4(); -uint8_t getRandom4() -{ - if (__randomIdx < 4) - { - __randomBuffer = getRandom32(); - __randomIdx = 32; - } - uint8_t rv = __randomBuffer & 0x0F; - __randomBuffer >>= 4; - __randomIdx -= 4; - return rv; -} - - -uint8_t getRandom5() -{ - if (__randomIdx < 5) - { - __randomBuffer = getRandom32(); - __randomIdx = 32; - } - uint8_t rv = __randomBuffer & 0x1F; - __randomBuffer >>= 5; - __randomIdx -= 5; - return rv; -} - - -uint8_t getRandom6() -{ - if (__randomIdx < 6) - { - __randomBuffer = getRandom32(); - __randomIdx = 32; - } - uint8_t rv = __randomBuffer & 0x3F; - __randomBuffer >>= 6; - __randomIdx -= 6; - return rv; -} +uint8_t getRandom5(); +uint8_t getRandom6(); // typical use -uint8_t throwDice() -{ - if (__randomIdx < 16) - { - __randomBuffer = getRandom32(); - __randomIdx = 32; - } - uint16_t rv = __randomBuffer % 6 + 1; - __randomBuffer >>= 3; - __randomIdx -= 3; - return rv; -} +uint8_t throwDice(); +uint8_t getRandom8(); -uint8_t getRandom8() -{ - if (__randomIdx < 8) - { - __randomBuffer = getRandom32(); - __randomIdx = 32; - } - uint8_t rv = __randomBuffer & 0xFF; - __randomBuffer >>= 8; - __randomIdx -= 8; - return rv; -} +uint16_t getRandom16(); +uint32_t getRandom24(); -uint16_t getRandom16() -{ - if (__randomIdx < 16) - { - __randomBuffer = getRandom32(); - __randomIdx = 32; - } - uint16_t rv = __randomBuffer & 0xFFFF; - __randomBuffer >>= 16; - __randomIdx -= 16; - return rv; -} - - -uint32_t getRandom24() -{ - return getRandom32() & 0xFFFFFF; -} - - -uint64_t getRandom64() -{ - uint64_t rv = getRandom32(); - rv <<= 32; - rv |= getRandom32(); - return rv; -} +uint64_t getRandom64(); /* // works well for 1..16; but above it is worse @@ -208,32 +76,8 @@ uint32_t getRandomBits(uint8_t n) // n = 1..31 // TODO: performance gain too low for n > 16 -uint32_t getRandomBits(uint8_t n) -{ - uint32_t rv = 0; +uint32_t getRandomBits(uint8_t n); - // for large values of n the more straightforward approach is faster (UNO). - if (n > 32) n = 32; - if (n >= 20) return getRandom32() >> (32 - n); - - if (n >= __randomIdx) - { - if (__randomIdx > 0) - { - n -= __randomIdx; - rv = __randomBuffer << n; - } - __randomBuffer = getRandom32(); - __randomIdx = 32; - } - if (n > 0) // more bits needed? - { - rv |= __randomBuffer & ((1UL << n) - 1); - __randomBuffer >>= n; - __randomIdx -= n; - } - return rv; -} // -- END OF FILE --