How to Calculate and Get Digit Using Module 11 Algorithm - php

I'm trying to do a syntax cast where I have a javascript function that calculates the check digit from an input number.
where the variables:
input - is the input value (Example: 200300)
num_digits - is the number of digits (if defined 1, its respective digit will be 7; already defined it 2, its respective digit will be 70. According to the input value)
limit - is the multiplication limit (in my case I need it to be multiplied by/up to 9)
x10 - in this case being true or false, being true the digit will be multiplied by 10
all variables mentioned above refer to my JavaScript function:
function calcDigitMod11(input, num_digits, limit, x10) {
var mult, sum, i, n, digit;
if (!x10) num_digits = 1;
for (n = 1; n <= num_digits; n++) {
sum = 0; mult = 2;
for (i = (input.length - 1); i >= 0; i--) {
sum += (mult * parseInt(input.charAt(i)));
if (++mult > limit) mult = 2;
}
if (x10) {
digit = ((sum * 10) % 11) % 10;
} else {
digit = sum % 11;
if (digit == 10) digit = 'x';
}
input += (digit);
}
return input.substring((input.length - num_digits), num_digits);
}
in short, my big problem is to create this same function in the php syntax
If you print the function's return on the console, passing the following parameters: calcDigitMod11(200300, 1, 9, true); your return must be the check digit
7
giving an applied...
"tried" to convert syntaxes using the same variables, parameters, among others
function calcDigitMod11($input, $num_digits, $limit, $x10) {
$mult; $sum; $i; $n; $digit;
if (!$x10) $num_digits = 1;
for ($n = 1; $n <= $num_digits; $n++) {
$sum = 0; $mult = 2;
for ($i = (strlen($input) - 1); $i >= 0; $i--) {
$sum += ($mult * (int)$input[$i]);
if ((++$mult) > $limit) $mult = 2;
}
if ($x10) {
$digit = (($sum * 10) % 11) % 10;
} else {
$digit = $sum % 11;
if ($digit == 10) $digit = 'x';
}
$input += ($digit);
}
return substr($input, strlen($input) - $num_digits, $num_digits);
}
I wrote an echo calcDigitMod11(200300, 1, 9, true); but it returns the digit to me.
0
I didn't find where the mistaken point x is, I don't know if I'm running away from logic! Here is a table of the JavaScript function of the true digits according to the input value:
calcDigitMod11(200300, 1, 9, true); is return 7
calcDigitMod11(200301, 1, 9, true); is return 5
calcDigitMod11(200302, 1, 9, true); is return 3
calcDigitMod11(200303, 1, 9, true); is return 1

In PHP, you can retrieve characters using index values only for strings. Example:
$str = "98765";
echo $str[1]; // will return 8
If we do the same for integers it will throw a warning.
$num = 98765;
echo $num[1];
Warning: Trying to access array offset on value of type int
To enable errors/warnings in your script, add the below code at the top of your php file
error_reporting(E_ALL);
ini_set('display_errors', '1');
Now, to fix your algorithm, we can use str_split to convert the integer values into an array before using index to extract digits.
<?php
function calcDigitMod11($input, $num_digits, $limit, $x10) {
$mult; $sum; $i; $n; $digit;
if (!$x10) $num_digits = 1;
for ($n = 1; $n <= $num_digits; $n++) {
$sum = 0; $mult = 2;
$input_arr = str_split($input); // Convert string to an array
for ($i = (strlen($input) - 1); $i >= 0; $i--) {
$sum += ($mult * $input_arr[$i]); // Use array to fetch digits
if ((++$mult) > $limit) $mult = 2;
}
if ($x10) {
$digit = (($sum * 10) % 11) % 10;
} else {
$digit = $sum % 11;
if ($digit == 10) $digit = 'x';
}
$input += ($digit);
}
echo "<br/><br/>True Digit=$digit<br/>";
return substr($input, strlen($input) - $num_digits, $num_digits);
}
echo "Output=" . calcDigitMod11(200300, 1, 9, true);
echo "Output=" . calcDigitMod11(200301, 1, 9, true);
echo "Output=" . calcDigitMod11(200302, 1, 9, true);
echo "Output=" . calcDigitMod11(200303, 1, 9, true);
?>
Output:
True Digit=7
Output=7
True Digit=5
Output=6
True Digit=3
Output=5
True Digit=1
Output=4
Working Demo

Related

PHP : Find list of X possible values which sum gives Y number

I want to solve a problem but I don't know how to proceed.
In fact, I want to create the following function:
<?php
function xSumY(int $x, int $y)
{
$set = [];
//find $set {$a + $b + $c + ... ($x times)} which sum equal $y
}
//Examples
$set1 = xSumY(55, 1);
$set2 = xSumY(1, 20);
$set3 = xSumY(3, 10); //returns for example {1, 3, 6}
NOTE: If possible avoid repetition of given value
Thanks in advance for your help.
I don't understand the need for all that code you used in your answer. I made this in five minutes:
function xSumY(int $x, int $y)
{
if ($y < $x) die("xSumY(): Invalid input, y < x.");
$r = array_fill(1, $x, 1);
for ($i = 1; $i <= $y - $x; $i++) {
$r[random_int(1, $x)]++;
}
return $r;
}
and it seems to do the job.
After several hours I got something like this:
static function xSumY(int $x, int $y)
{
$setOf = []; //Will contain sum subset
$roundTo = strlen($x); //Will help to round values
if($x < 1 || $y < 1): //return empty array if null or negative values
return $setOf;
elseif($x == $y): //fill with same value if $x = $y
$setOf = array_fill(0, $x, ($x/$y));
return $setOf;
endif;
$maxRand = round($y/$x, $roundTo); //we are going to find a value between 0 and this
$multiplicator = pow((1 * 10), $roundTo+1);
$max = $maxRand * $multiplicator;
for($i=1; $i <= $x; $i++): //this loop help to create our set
if(count($setOf) + 1 == $x):
$currentElement = round(($y - array_sum($setOf)), $roundTo); //the last value of the set
else:
$currentElement = round(mt_rand(1, $max) / $multiplicator);
endif;
array_push($setOf, $currentElement); //add values to our set
endfor;
shuffle($setOf); //Make our set look more real
while(round((min($setOf) / max($setOf)) * 100) < 20): //Reduce the difference & avoid zero
$maxSet = max($setOf);
$maxIndex = array_search(max($setOf), $setOf);
$minSet = min($setOf);
$minIndex = array_search(min($setOf), $setOf);
$falsAverage = $maxSet + $minSet;
$newMin = round((mt_rand(2, 4) / 10) * $falsAverage);
$newMax = $falsAverage - $newMin;
$setOf[$maxIndex] = $newMax;
$setOf[$minIndex] = $newMin;
endwhile;
return $setOf; //Our set ready for use
}
I want to know if it's good or I'm missing something or if there is a way I can dot it better. Also, I wanted to know if this operation will not excessively consume the memory.

Calculate normal distribution probability in php

I need to use this table to get the probability from a given z value:
https://statistics.laerd.com/statistical-guides/img/normal-table-large.png
I'm sure there should be a better way to get those values on php but don't know how to calculate them.
Any help would be very appreciated.
Cumulative distribution function of the standard normal distribution with precision calculation:
function cumulativeNormalDensity($x, $mean = 0, $stddev = 1, $precision = 48) {
$result = 0;
$x -= $mean;
$x /= $stddev;
for ($i = 0, $k = 1; $i < $precision; $i++) {
$n = $i * 2 + 1;
$k *= $n;
$p = pow($x, $n) / $k;
if (is_nan($p) || is_infinite($p)) break;
$result += $p;
}
$result *= 1 / sqrt(2 * pi()) * exp(-pow($x, 2) / 2);
$result += 0.5;
return max(0, $result);
}
Table, I believe, is CDF of the normal distribution. I coded it using expression for
error function approximation.
Code is untested!
function sgn( $x ) {
if ( $x < 0 )
return -1;
return 1;
}
function erf( $x ) {
$e = exp(-$x*$x);
$e2 = exp(-$x*$x*2);
$q = sqrt(pi())/2 + 31*$e/200 - 341*$e2/8000;
return 2*sgn($x)*sqrt(1-$e)*$q/sqrt(pi());
}
function CDF( $x ) {
return (1 + erf($x / sqrt(2))) / 2;
}
print_r(CDF(0));
print_r(CDF(0.1));
....
UPDATE
Here is quick (untested!) code to compute erf() with up to 4 terms
function erf( $x ) {
$e = exp(-$x*$x);
$t = 1.0 - $e;
$s = 1. + $t*(-1./12. + $t*(-7./480. + $t*( -5./896. +$t * (-787./276480.))));
return 2.*sgn($x)*sqrt($t)*$s/sqrt(pi());
}

Random, but should not exceed total

rand(1,5)
.. generates random numbers for example: 4 3 2 3 2 (sum is equal 14).
I want the total to NOT exceed x (which is say 5), so in this case, it could be:
1 + 2 + 2 = 5
2 + 3 = 5
and so on ... variable length as long as sum < x
Should I generate a random, check against x, generate another, check again or is there another way?
The most obvious way is just to keep looping and generating a smaller and smaller random number until you're capped out.
$min = 2;
$max = 5;
$randoms = [];
while ($max > $min) {
$max -= ( $rand = rand($min, $max) );
$randoms[] = $rand;
}
Updated for the actual use-case (see comments):
function generateRandomSpend($balance, $maximum = 5, $minimum = 2) {
$amounts = array();
while ($balance) {
// If we can't add any more minimum-spends, stop
if ($balance - $minimum < 0) {
break;
} else {
// Don't generate pointlessly-high values
$maximum = min($balance, $maximum);
$balance -= $amounts[] = rand($minimum, $maximum);
}
}
return $amounts;
}
print_r( $s = generateRandomSpend(10) );
You can also do
echo array_sum( generateRandomSpend(10) );
to get a numeric value of their total spend.
This is also working and give result which you want
<?php
$fixed = 5;
$random = array();
$no = 0;
while(1) {
$number = rand(1,5);
$no +=$number;
if($no > $fixed) {
break;
}
$random[]= $number;
}
?>

prevent Random PHP generator from including zeros

I have a php script that creates a random 10 digit order number:
// Assign order number length
$digits = 10;
// Create random order number to be stored with this order
$order_number = rand(pow(10, $digits-1), pow(10, $digits)-1);
How do I prevent this from ever including the digit zero 0 in the random 10 digit number? Thanks in advance!
You can do fancy base conversions, but in the end, the most straightforward way is to just get a string:
function random_string($count, $available) {
$result = '';
$max = strlen($available) - 1;
for($i = 0; $i < $count; $i++) {
$result .= $available[rand(0, $max)];
}
return $result;
}
…
$order_number = random_string($digits, '123456789');
You can treat it as a number of base 9
base_convert(rand(0, pow(9, $digits) - 1), 10, 9)
This will give you numbers with digits from 0 to 8.
Now just add 1 to every digit to make it 1 to 9
(pow(10, $digits) - 1) / 9
will give you a number filled with ones. Now just add it to your previous number and there you go:
$digits = 10;
$order_number = (pow(10, $digits) - 1) / 9 + base_convert(rand(0, pow(9, $digits) - 1), 10, 9);
Try this :D
function getRandom($from, $to){
$num = rand($from, $to);
$have_zero = true;
$strNum = strval($num);
while ($have_zero){
$have_zero = false;
for ($i = 0; $i < sizeof($strNum); $i++){
if ($strNum[$i] == '0'){
$have_zero = true;
$num = rand($from, $to);
$strNum = strval($num);
break;
}
}
}
return $num;
}
getRandom(1111111111, 9999999999);
You could use a simple function like this:
function getRandom($length) {
$numbers = '';
for($i = 0; $i < $length; $i++) {
$numbers .= rand(1, 9);
}
return $numbers;
}
echo getRandom(10);
I would make a function.
<?php
function myRandomNumberWithoutZeros($digits)
{
$result = str_replace("0", "",rand(pow(10,$digits-1), pow(10, $digits)-1)."");
$resultLength = strlen($result);
if($resultLength < $digits)
{
return intval($result.myRandomNumberWithoutZeros($digits-$resultLength));
}
return intval($result);
}
echo myRandomNumberWithoutZeros(10);
?>

Auto incrementing unique url

I am looking to create an auto incrementing unique string using PHP, containing [a-Z 0-9] starting at 2 chars long and growing when needed.
This is for a url shrinker so each string (or alias) will be saved in the database attached to a url.
Any insight would be greatly appreciated!
Note this solution won't produce uppercase letters.
Use base_convert() to convert to base 36, which will use [a-z0-9].
<?php
// outputs a, b, c, ..., 2o, 2p, 2q
for ($i = 10; $i < 99; ++$i)
echo base_convert($i, 10, 36), "\n";
Given the last used number, you can convert it back to an integer with intval() increment it and convert the result back to base 36 with base_convert().
<?php
$value = 'bc9z';
$value = intval($value, 36);
++$value;
$value = base_convert($value, 10, 36);
echo $value; // bca0
// or
echo $value = base_convert(intval($value, 36) + 1, 10, 36);
Here's an implementation of an incr function which takes a string containing characters [0-9a-zA-Z] and increments it, pushing a 0 onto the front if required using the 'carry-the-one' method.
<?php
function incr($num) {
$chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$parts = str_split((string)$num);
$carry = 1;
for ($i = count($parts) - 1; $i >= 0 && $carry; --$i) {
$value = strpos($chars, $parts[$i]) + 1;
if ($value >= strlen($chars)) {
$value = 0;
$carry = 1;
} else {
$carry = 0;
}
$parts[$i] = $chars[$value];
}
if ($carry)
array_unshift($parts, $chars[0]);
return implode($parts);
}
$num = '0';
for ($i = 0; $i < 1000; ++$i) {
echo $num = incr($num), "\n";
}
If your string was single case rather than mixed, and didn't contain numerics, then you could literally just increment it:
$testString="AA";
for($x = 0; $x < 65536; $x++) {
echo $testString++.'<br />';
}
$testString="aa";
for($x = 0; $x < 65536; $x++) {
echo $testString++.'<br />';
}
But you could possibly make some use of this feature even with a mixed alphanumeric string
To expand on meagar's answer, here is how you can do it with uppercase letters as well and for number arbitrarily big (requires the bcmath extension, but you could as well use gmp or the bigintegers pear package):
function base10ToBase62($number) {
static $chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
$result = "";
$n = $number;
do {
$remainder = bcmod($n, 62);
$n = bcdiv($n, 62);
$result = $chars[$remainder] . $result;
} while ($n > 0);
return $result;
}
for ($i = 10; $i < 99; ++$i) {
echo base10ToBase62((string) $i), "\n";
}

Categories