I want to calculate Frequency (Monobits) test in PHP:
Description: The focus of the test is
the proportion of zeroes and ones for
the entire sequence. The purpose of
this test is to determine whether that
number of ones and zeros in a sequence
are approximately the same as would be
expected for a truly random sequence.
The test assesses the closeness of the
fraction of ones to ½, that is, the
number of ones and zeroes in a
sequence should be about the same.
I am wondering that do I really need to calculate the 0's and 1's (the bits) or is the following adequate:
$value = 0;
// Loop through all the bytes and sum them up.
for ($a = 0, $length = strlen((binary) $data); $a < $length; $a++)
$value += ord($data[$a]);
// The average should be 127.5.
return (float) $value/$length;
If the above is not the same, then how do I exactly calculate the 0's and 1's?
No, you really need to check all zeroes and ones. For example, take the following binary input:
01111111 01111101 01111110 01111010
. It is clearly (literally) one-sided(8 zeroes, 24 ones, correct result 24/32 = 3/4 = 0.75) and therefore not random. However, your test would compute 125.0 /255 which is close to ½.
Instead, count like this:
function one_proportion($binary) {
$oneCount = 0;
$len = strlen($binary);
for ($i = 0;$i < $len;$i++) {
$intv = ord($binary{$i});
for ($bitp = 0;$bitp < 7;$bitp++) {
$oneCount += ($intv>>$bitp) & 0x1;
}
}
return $oneCount / (8 * $len);
}
Related
For an endless number such as Pi, how would one go about finding the first occurrence of an exact sum of digits for a given number n.
For example. If n=20
Pi=3.14159265358979323846264338327950288419716939...
then the first occurrence is from digit 1 to digit 5 since:
1+4+1+5+9=20
if n=30, then the first occurrence is from digit 5 to digit 11
since 9+2+6+5+3+5=30
answer should have a working php demo
The answer to this is using sliding window that will maintain the sum. so maintain two pointers say i and j. Keep increasing j and adding the elements inside. when it crosses the desired sum increase i and decrease the element at i. Then keep increasing j until the sum is reached or the sum overflows so you repeat the above process.
Example sum = 30
141592653589793238 >> i=j=0 current_sum = 1
141592653589793238 >> i=0 j=6 current_sum=28
in the next iteration adding 5 will result in current_sum>30 so hence you increment i
141592653589793238 >> i=1 j=6 current_sum=27
141592653589793238 >> i=2 j=6 current_sum=23
141592653589793238 >> i=2 j=7 current_sum=28
Keep going in this manner and it will finally reach the window that is equal to the sum =30 . That should break you out of the loop and help you find the answer.
Method 1 (suggested by Ashwin Bhat)
This implementation uses two pivots. The sum of digits between $pivot_a and $pivot_b is computed. Depending on the value of the sum, we increment $pivot_b (if the sum is less) or $pivot_a (if the sum is greater). If the sum is equal to $n, break. The values of the pivots give the appropriate digit indices.
$pi = "314159265358979323846264338327950288419716939";
$n = 30;
$pivot_a = $pivot_b = 0;
$sum = 0;
for( ; $pivot_b < strlen($pi); ) {
if($sum < $n) {
$sum += $pi[$pivot_b++];
} elseif ($sum > $n) {
$sum -= $pi[$pivot_a++];
} else {
print('Solution found from digit '.$pivot_a.' to '.$pivot_b.'.');
exit;
}
}
print('No match was found.');
Method 2
This implementation uses one pivot only, from which it starts summing up the digits. If the sum happens to be greater than the desired value, it resets the sum to zero, shifts the pivot one position and starts the summing again.
$pi = "314159265358979323846264338327950288419716939";
$n = 30;
// Let's sum up all the elements from $pivot until we get the exact sum or a
// number greater than that. In the latter case, shift the $pivot one place.
$pivot = 0;
$sum = 0;
for($k=0 ; $sum != $n && $k < strlen($pi) ; $k++) {
$sum += $pi[$k];
print($pi[$k]);
if($sum > $n) {
print(' = '.$sum.' fail, k='.($pivot+1).PHP_EOL);
$sum = 0;
$k = $pivot++;
} elseif($sum < $n) {
print("+");
}
}
print(' = '.$n.' found from digit '.$pivot.' to '.$k.'.');
The implementation is not very effective but tries to explain the steps. It prints
3+1+4+1+5+9+2+6 = 31 fail, k=1
1+4+1+5+9+2+6+5 = 33 fail, k=2
4+1+5+9+2+6+5 = 32 fail, k=3
1+5+9+2+6+5+3 = 31 fail, k=4
5+9+2+6+5+3 = 30 found from digit 4 to 10.
Here's another approach. It builds an array of sums along the way and, on every iteration, attempts to add the current digit to the previous sums, and so on, while always only keeping the sums that are still relevant (< target).
The function either returns:
an array of 2 values representing the 0-based index interval within the digits,
or null if it couldn't find the target sum
Code:
function findFirstSumOccurrenceIndexes(string $digits, int $targetSum): ?array
{
$sums = [];
for ($pos = 0, $length = strlen($digits); $pos < $length; $pos++) {
$digit = (int)$digits[$pos];
if ($digit === $targetSum) {
return [$pos, $pos];
}
foreach ($sums as $startPos => $sum) {
if ($sum + $digit === $targetSum) {
return [$startPos, $pos];
}
if ($sum + $digit < $targetSum) {
$sums[$startPos] += $digit;
}
else {
unset($sums[$startPos]);
}
}
$sums[] = $digit;
}
return null;
}
Demo: https://3v4l.org/9t3vf
I need to convert a really big integer that is represented as a string to a binary string (aka normal integer, but it is always bigger as normal php integer can hold) to efficiently store it in database and have a unique index on it.
The number comes from GMP (gmp_strval()) and may have different lengths, usually about 200-300 "characters" in it, so it never fits into PHP integer. The idea is to convert it into a binary string representing an integer, kind of big integer. Can I do it with PHP?
Sure you can do this.
Remember how to convert a decimal number to binary by hand.
look if the last digit is even (gives a 0) or odd (gives a 1)
subtract the 1, if you get one.
divide by 2. This have to be done digit by digit as in elementary school :-)
repeat this until your decimalnumber become zero.
I wrote a function for this
function strMod2(array $dec)
{
return ((int)end($dec)) % 2;
}
function strDivBy2(array $dec)
{
$res = [];
$carry = 0;
if($dec[0] == '0')
array_shift($dec);
$len = count($dec);
for($i = 0; $i < $len; $i++)
{
$num = $carry*10 + ((int)$dec[$i]);
$carry = $num % 2;
$num -= $carry;
$res[] = $num / 2;
}
return $res;
}
function dec2bin_str($dec)
{
$dec_arr = str_split($dec);
$bin_arr = [];
while(count($dec_arr) > 1 || $dec_arr[0] != 0)
{
array_unshift($bin_arr, strMod2($dec_arr));
$dec_arr = strDivBy2($dec_arr);
}
return implode($bin_arr);
}
You can use it as
echo dec2bin_str('5'); // '101'
echo dec2bin_str('146456131894613465451'); // '1111111000001111100101101000000000000010100001100010101100101101011'
Maybe this can be done faster by using a library for big integers.
Found Math_BigInteger library that can do it:
$a = new Math_BigInteger($intString);
$base256IntString = $a->toBytes();
https://github.com/pear/Math_BigInteger
I need to generate x amount of random odd numbers, within a given range.
I know this can be achieved with simple looping, but I'm unsure which approach would be the best, and is there a better mathematical way of solving this.
EDIT: Also I cannot have the same number more than once.
Generate x integer values over half the range, and for each value double it and add 1.
ANSWERING REVISED QUESTION: 1) Generate a list of candidates in range, shuffle them, and then take the first x. Or 2) generate values as per my original recommendation, and reject and retry if the generated value is in the list of already generated values.
The first will work better if x is a substantial fraction of the range, the latter if x is small relative to the range.
ADDENDUM: Should have thought of this approach earlier, it's based on conditional probability. I don't know php (I came at this from the "random" tag), so I'll express it as pseudo-code:
generate(x, upper_limit)
loop with index i from upper_limit downto 1 by 2
p_value = x / floor((i + 1) / 2)
if rand <= p_value
include i in selected set
decrement x
return/exit if x <= 0
end if
end loop
end generate
x is the desired number of values to generate, upper_limit is the largest odd number in the range, and rand generates a uniformly distributed random number between zero and one. Basically, it steps through the candidate set of odd numbers and accepts or rejects each one based how many values you still need and how many candidates still remain.
I've tested this and it really works. It requires less intermediate storage than shuffling and fewer iterations than the original acceptance/rejection.
Generate a list of elements in the range, remove the element you want in your random series. Repeat x times.
Or you can generate an array with the odd numbers in the range, then do a shuffle
Generation is easy:
$range_array = array();
for( $i = 0; $i < $max_value; $i++){
$range_array[] .= $i*2 + 1;
}
Shuffle
shuffle( $range_array );
splice out the x first elements.
$result = array_slice( $range_array, 0, $x );
This is a complete solution.
function mt_rands($min_rand, $max_rand, $num_rand){
if(!is_integer($min_rand) or !is_integer($max_rand)){
return false;
}
if($min_rand >= $max_rand){
return false;
}
if(!is_integer($num_rand) or ($num_rand < 1)){
return false;
}
if($num_rand <= ($max_rand - $min_rand)){
return false;
}
$rands = array();
while(count($rands) < $num_rand){
$loops = 0;
do{
++$loops; // loop limiter, use it if you want to
$rand = mt_rand($min_rand, $max_rand);
}while(in_array($rand, $rands, true));
$rands[] = $rand;
}
return $rands;
}
// let's see how it went
var_export($rands = mt_rands(0, 50, 5));
Code is not tested. Just wrote it. Can be improved a bit but it's up to you.
This code generates 5 odd unique numbers in the interval [1, 20]. Change $min, $max and $n = 5 according to your needs.
<?php
function odd_filter($x)
{
if (($x % 2) == 1)
{
return true;
}
return false;
}
// seed with microseconds
function make_seed()
{
list($usec, $sec) = explode(' ', microtime());
return (float) $sec + ((float) $usec * 100000);
}
srand(make_seed());
$min = 1;
$max = 20;
//number of random numbers
$n = 5;
if (($max - $min + 1)/2 < $n)
{
print "iterval [$min, $max] is too short to generate $n odd numbers!\n";
exit(1);
}
$result = array();
for ($i = 0; $i < $n; ++$i)
{
$x = rand($min, $max);
//not exists in the hash and is odd
if(!isset($result{$x}) && odd_filter($x))
{
$result[$x] = 1;
}
else//new iteration needed
{
--$i;
}
}
$result = array_keys($result);
var_dump($result);
How can I generate fix smaller random numbers from a large number. Addition of these smaller numbers must be equal to large number. Suppose I want to generate 400 random number and addition of these smaller number = e.g. 1,000,000. every number should be unique and have any value assign to it. Like Number 1=1000 and number 2 may contain only 5. But total of all the number must be a large number. Is there any algorithm to do this kind of operation in php?
function array_generate_sum($n, $total)
{
$sum = 0;
$arr = array();
for( ; $n >= 0; $n--)
{
$current = $n == 0 ? $total - $sum : mt_rand(1, $total - $sum - $n);
$sum += $current;
$arr[] = $current;
}
return $arr;
}
// Generate an array of 5 values whose sum is 30
array_generate_sum(5, 30);
I want a random number generator with non-uniform distribution, ie:
// prints 0 with 0.1 probability, and 1 with 0.9 probability
echo probRandom(array(10, 90));
This is what I have right now:
/**
* method to generated a *not uniformly* random index
*
* #param array $probs int array with weights
* #return int a random index in $probs
*/
function probRandom($probs) {
$size = count($probs);
// construct probability vector
$prob_vector = array();
$ptr = 0;
for ($i=0; $i<$size; $i++) {
$ptr += $probs[$i];
$prob_vector[$i] = $ptr;
}
// get a random number
$rand = rand(0, $ptr);
for ($i=0, $ret = false; $ret === false; $i++) {
if ($rand <= $prob_vector[$i])
return $i;
}
}
Can anyone think of a better way? Possibly one that doesn't require me to do pre-processing?
If you know the sum of all elements in $probs, you can do this without preprocessing.
Like so:
$max = sum($probs);
$r = rand(0,$max-1);
$tot = 0;
for ($i = 0; $i < length($probs); $i++) {
$tot += $probs[$i];
if ($r < $tot) {
return $i;
}
}
This will do what you want in O(N) time, where N is the length of the array. This is a firm lower bound on the algorithmic runtime of such an algorithm, as each element in the input must be considered.
The probability a given index $i is selected is $probs[$i]/sum($probs), given that the rand function returns independent uniformly distributed integers in the given range.
In your solution you generate an accumulated probability vector, which is very useful.
I have two suggestions for improvement:
if $probs are static, i.e. it's the same vector every time you want to generate a random number, you can preprocess $prob_vector just once and keep it.
you can use binary search for the $i (Newton bisection method)
EDIT: I now see that you ask for a solution without preprocessing.
Without preprocessing, you will end up with worst case linear runtime (i.e., double the length of the vector, and your running time will double as well).
Here is a method that doesn't require preprocessing. It does, however, require you to know a maximum limit of the elements in $probs:
Rejection method
Pick a random index, $i and a random number, X (uniformly) between 0 and max($probs)-1, inclusive.
If X is less than $probs[$i], you're done - $i is your random number
Otherwise reject $i (hence the name of the method) and restart.