Php binary radix sort implementation - php

I'm trying to implement a radix sort in binary, because i want to check if the speed of bit shifting operations counterbalance the number of steps required.
My counting sort seems to work, but as soon as i have several passes for the radix sort, the result break.
Any help greatly appreciated.
/*
* bits is to store the value of the current digit for each number in the input array
*/
function countSort(&$arr, $digit) {
$bits = $output = array_fill(0, NB_ELEMS, 0);
$count = [0,0];
// Store count of occurrences in count[]
for ($i = 0; $i < NB_ELEMS; $i++) {
$nb = $arr[$i];
$bit = ($nb >> $digit) & 1;
$bits[$i] = $bit;
$count[$bit]++;
}
// Cumulative count
$count[1] = NB_ELEMS;
// Rearranging
for ($i = 0; $i < NB_ELEMS; $i++) {
$nb = $arr[$i];
$bit = $bits[$i];
$output[$count[$bit] - 1] = $nb;
$count[$bit]--;
}
// Switch arrays
$arr = $output;
}
function radixSort(&$arr, $nb_digits) {
// Do counting sort for every binary digit
for($digit = 0; $digit < $nb_digits; $digit++) {
countSort($arr, $digit);
}
}
$tab = [4,3,12,8,7];
$max_value = max($tab);
$nb_digits = floor(log($max_value, 2)) + 1;
radixSort($tab, $nb_digits);

Ok, thanks to a friend, i found my problem : the counting sort implementation which i use stacks the same digit numbers using the counter as top index, and then decrementing it. And that's ok for counting sort (so one iteration), but it scrambles everything when you use it in radix sort (multiple counting sort iterations).
So i used instead a method in which you shift your counters one time right, which gives you for the n+1 digit your starting point, and then increment it.
And boom you're good to go.
function countSort2(&$arr, $digit) {
$bits = $output = array_fill(0, NB_ELEMS, 0); // output array
$count = [0,0];
// Store count of occurrences in count[]
for ($i = 0; $i < NB_ELEMS; $i++) {
$nb = $arr[$i];
$bit = ($nb >> $digit) & 1;
$bits[$i] = $bit;
$count[$bit]++;
}
// Cumulative count shifted
$count[1] = $count[0];
$count[0] = 0;
// Rearranging
for ($i = 0; $i < NB_ELEMS; $i++) {
$nb = $arr[$i];
$bit = $bits[$i];
$output[$count[$bit]] = $nb;
$count[$bit]++;
}
// Switch arrays
$arr = $output;
}
I also made some tests (1M items, max value 1M, 100 iterations), and a method storing values instead of counting them, and merging thereafter seems twice as fast (function just after that). Anyway, both methods are far under native sort performance.
Any comment appreciated
function byDigitSortArrayMerge(&$arr, $digit) {
$output = array_fill(0, NB_ELEMS - 1, 0); // output array
$bits = array_fill(0, NB_ELEMS - 1, 0); // output array
$by_bit = [[], []];
// Store count of occurrences in count[]
for ($i = 0; $i < NB_ELEMS; $i++) {
$nb = $arr[$i];
$bit = ($nb >> $digit) & 1;
$by_bit[$bit][] = $nb;
}
$arr = array_merge($by_bit[0], $by_bit[1]);
}

Related

PHP - get random integer using random_int() without repeating and looping

I need to get 50 random numbers out of range 1-100 without repeating. The current way i do is :
$array = array();
while (count($array) <= 50) {
$temp = random_int(1,100);
if (!in_array($temp, $array))
$array[] = $temp;
}
However, the looping is too many because I need to generate for more than 100,000 times.
Is there other ways that I can get a 50 random non-repeating numbers without looping ?
For example:
$number= range(1,100);
$array = array_slice(shuffle($number),0,50);
I can't use shuffle because it uses pseudo random number.
Is there other ways to achieve what I need, or ways that could shorten time.
pre fill a array of numbers and pick from them, and then remove it.
it prevents the unnecessary random generations you have
$numbers = [];
for ($i = 1; $i <= 100; $i++) {
$numbers[] = $i;
}
$randomNumbers = [];
for ($i = 1; $i <= 50; $i++) {
$r = rand(0, count($numbers) - 1);
$randomNumbers[] = $numbers[$r];
array_splice($numbers, $r, 1);
}
This would be my approach:
This gives you 50 numbers in any case, and they are defenitely different from each other. PLUS: you dont have to prefill some other array:
$start = microtime(true);
for($i = 0; $i <= 100000; $i++){
$arr = [];
while(sizeof($arr) < 50){
$num = rand(1, 100);
$arr[$num] = $num;
}
if(array_unique($arr) !== $arr || sizeof($arr) !== 50 ){
print("FAIL");
}
//print(array_unique($arr) == $arr ? "true" : "false");print("<br>");
//print(sizeof($arr));print("<br>");
//print_r(array_count_values ($arr));print("<br>");
//print_r($arr);print("<br>");
}
$time_elapsed_secs = microtime(true) - $start;
print($time_elapsed_secs);print("<br>");
Running this 100000 times takes about 0.4sec for me.
The actual generation is done in this part:
$arr = [];
while(sizeof($arr) < 50){
$num = rand(1, 100);
$arr[$num] = $num;
}
We can do in 2 steps:
$x = 0;
$arr = [];
while($x < 50){
$tmp = rand(1, 100);
if(!in_array($tmp, $arr)){
$arr[] = $tmp;
$x++;
}
}

How to generate a random pattern of *&# in PHP

Hello there i am making a program which will let me help generate a random string with a specified limit and random strings of *&# but then the combination of *&# should not repeat.
Ex: if I input 3 then the O/P should be
#**
**#
**#
It should generate a random string of length 3 up to 3 rows with different patterns also the pattern should not repeat. I am using the below code but not able to attain it.
$n = 3;
for($i = 0; $i < n; $i++)
{
for($j=0;$j<=$n;j++)
{
echo "*#";
}
echo "<br />";
}
But I am not able to generate the output, where is my logic failing?
If you want to make sure the same pattern doesn't show up more than once you'll have to keep a record of the generated strings. In the most basic form it could look like this:
public function generate() {
$amount = 3; // The amount of strings you want.
$generated_strings = []; // Keep a record of the generated strings.
do {
$random = $this->generateRandomString(); // Generate a random string
if(!in_array($random, $generated_strings)) { // Keep the record if its not already present.
$generated_strings[] = $random;
}
} while(sizeof($generated_strings) !== $amount); // Repeat this process until you have three strings.
print_r($generated_strings);
}
public function generateRandomString($length = 3) {
$characters = '*&#';
$charactersLength = strlen($characters);
$randomString = '';
for ($i = 0; $i < $length; $i++) {
$randomString .= $characters[rand(0, $charactersLength - 1)];
}
return $randomString;
}
Not necessarily the most optimized algorithm but it should work.
I am using a string generator, somewhat random, combining the chars you have provided. The second part is filling the output array with generated strings that are not already present.
<?php
function randomize($n) {
$s = '';
for ($i = 0; $i < $n; $i++) {
$s. = (rand(0, 10) < 5 ? '*' : '#');
}
return $s;
}
$n = 3;
$output = array();
for ($i = 0; $i < $n; $i++) {
$tmp = randomize($n);
while (in_array($tmp, $output)) {
$tmp = randomize($n);
}
$output[] = $tmp;
}
print_r($output);
Visible here
You can use a while loop and array unique to do this.
I first have an array with possible chars.
Then I loop until result array is desired lenght.
I use array unique to remove any duplicates inside the loop.
I use rand(0,2) to "select" a random character from possible characters array.
$arr = ["*", "&", "#"];
$res = array();
$n =7;
While(count($res) != $n){
$temp="";
For($i=0;$i<$n;$i++){
$temp .= $arr[Rand(0,count($arr)-1)];
}
$res[] = $temp;
$res = array_unique($res);
}
Var_dump($res);
https://3v4l.org/Ko4Wd
Updated with out of scope details not clearly specified by OP.

Calculate from an array the number that is equal or higher and closest to a given number

I need to calculate from a given array the number that is equal or higher and closest to a given number in PHP. Example:
Number to fetch:
6.85505196
Array to calculate:
3.11350000
4.38350000
4.04610000
3.99410000
2.86135817
0.50000000
Only correct combination should be:
3.99410000 + 2.86135817 = 6.85545817
Can somebody help me? It's been 3 hours I'm getting mad!
UPDATE: I finally finished my code as following:
$arr = array(3.1135, 4.3835, 4.0461, 3.9941, 2.86135817, 0.5);
$fetch = 6.85505196;
$bestsum = get_fee($arr, $fetch);
print($bestsum);
function get_fee($arr, $fetch) {
$bestsum = 999999999;
$combo = array();
$result = array();
for ($i = 0; $i<count($arr); $i++) {
combinations($arr, $i+1, $combo);
}
foreach ($combo as $idx => $arr) {
$sum = 0;
foreach ($arr as $value) {
$result[$idx] += $value;
}
if ($result[$idx] >= $fetch && $result[$idx] < $bestsum) $bestsum = $result[$idx];
}
return $bestsum;
}
function combinations($arr, $level, &$combo, $curr = array()) {
for($j = 0; $j < count($arr); $j++) {
$new = array_merge($curr, array($arr[$j]));
if($level == 1) {
sort($new);
if (!in_array($new, $combo)) {
$combo[] = $new;
}
} else {
combinations($arr, $level - 1, $combo, $new);
}
}
}
I hope the following example might help you. Please try this
<?php
$array = array(
"3.11350000",
"4.38350000",
"4.04610000",
"3.99410000",
"2.86135817",
"0.50000000"
);
echo "<pre>";
print_r($array);// it will print your array
for($i=0; $i<count($array); $i++)
{
$j=$i+1;
for($j;$j<count($array); $j++)
{
$sum = $array[$i] + $array[$j];
// echo $array[$i]. " + ".$array[$j]." = ".$sum."<br>"; //this will display all the combination of sum
if($sum >= 6.85505196 && ($sum <= round(6.85505196)) )//change the condition according to your requirement
{
echo "The correct combinations are:<br/><br/>";
echo "<b>". $array[$i]. " + ".$array[$j]." = ".$sum."<b>";
echo "<br/>";
}
}
echo "<br/>";
}
?>
We will get the result as below
Array
(
[0] => 3.11350000
[1] => 4.38350000
[2] => 4.04610000
[3] => 3.99410000
[4] => 2.86135817
[5] => 0.50000000
)
The correct combinations are:
4.04610000 + 2.86135817 = 6.90745817
3.99410000 + 2.86135817 = 6.85545817
You should do it in two steps:
a. Work out (or look up) an algorithm to do the job.
b. Implement it.
You don't say what you've managed in the three hours you worked on this, so here's a "brute force" (read: dumb) algorithm that will do the job:
Use a variable that will keep your best sum so far. It can start out as zero:
$bestsum = 0;
Try all single numbers, then all sums of two numbers, then all sums of three numbers, etc.: Every time you find a number that meets your criteria and is better than the current $bestsum, set $bestsum to it. Also set a second variable, $summands, to an array of the numbers you used to get this result. (Otherwise you won't know how you got the solution). Whenever you find an even better solution, update both variables.
When you've tried every number combination, your two variables contain the best solution. Print them out.
That's all. It's guaranteed to work correctly, since it tries all possibilities. There are all sorts of details to fill in, but you can get to work and ask here for help with specific tasks if you get stuck.
Thank you all for your help!
My code is working pretty cool when is needed to fetch one or two numbers (addition) only. But can't figure out how to add more combinations up to the total count of elements in my given array.
I mean if there are, let's say, 8 numbers in my array I want to try all possible combinations (additions to each other) as well.
My actual code is:
$bestsum = 1000000;
for ($i = 0; $i < count($txinfo["vout"]); $i++) {
if ($txinfo["vout"][$i]["value"] >= $spent && $txinfo["vout"][$i]["value"] < $bestsum) {
$bestsum = $txinfo["vout"][$i]["value"];
}
}
for($i = 0; $i < count($txinfo["vout"]); $i++) {
$j = $i + 1;
for($j; $j < count($txinfo["vout"]); $j++) {
$sum = $txinfo["vout"][$i]["value"] + $txinfo["vout"][$j]["value"];
if($sum >= $spent && $sum < $bestsum) {
$bestsum = $sum;
}
}
}
$fee = bcsub($bestsum, $spent, 8);
print("Fee: ".$fee);
New updated code.
<?php
$x = 6.85505196;
$num = array(3.1135, 4.3835, 4.0461, 3.9941, 2.86135817, 0.5);
asort($num); //sort the array
$low = $num[0]; // lowest value in the array
$maxpossible = $x+$low; // this is the maximum possible answer, as we require the number that is equal or higher and closest to a given number
$num = array_values($num);
$iterations = $x/$num[0]; // possible combinations loop, to equate to the sum of the given number using the lowest number
$sum=$num;
$newsum = $sum;
$k=count($num);
for($j=0; $j<=$iterations; $j++){
$l = count($sum);
for($i=0; $i<$l; $i++){
$genSum = $sum[$j]+$sum[$i];
if($genSum <= $maxpossible){
$newsum[$k] = $genSum;
$k++;
}
}
$newsum = array_unique($newsum);
$newsum = array_values($newsum);
$k = count($newsum);
$sum = $newsum;
}
asort($newsum);
$newsum = array_values($newsum);
for($i=0; $i<count($newsum); $i++){
if($x<=$newsum[$i]){
echo "\nMaximum Possible Number = ".$newsum[$i];
break;
}
}
?>

PHP Radix Sort Algorithm - Type conversion ok?

This is a function I wrote for a Radix Sort in PHP.
It works for all cases where the array is populated with numbers greater than 0.
function radix_sort($arr) {
// Find the number of passes needed to complete the sort
$passes = strlen((string)max($arr));
$buckets = [];
// Start the passes
for($i = 1; $i <= $passes; $i++) {
// Create - reinitialize some buckets
for ($b = 0; $b <= 9; $b++) {
$buckets[$b] = [];
}
for ($j = 0; $j < count($arr); $j++) {
// Drop into the proper bucket based on the significant digit
$numStr = (string)$arr[$j];
if (strlen($numStr) < $i) {
$bucketsIndex = 0;
} else {
$bucketsIndex = $numStr[strlen($numStr) - $i];
}
array_push($buckets[$bucketsIndex], $arr[$j]);
}
// Repopulate our array by pulling out of our buckets
$k = 0;
foreach ($buckets as $bucket) {
foreach ($bucket as $value) {
$arr[$k] = $value;
$k++;
}
}
}
return $arr;
}
I feel like there's too much type converting going on. Is it ok to be doing this? If I wanted to pull the Nth digit from a large number, is there a better way in PHP?
You could convert all the value to string before the function, and then revert back to int at the end:
function radix_sort($arr) {
$arr = array_map ('strval', $arr) ;
$passes = strlen(max($arr)) ; // Should still work since all values are numeric
/* Do your stuff... */
return array_map ('intval', $arr) ;
}
Then in your inner loop you could store the length (I don't really know the complexity of strlen in PHP but I assumed it is not O(1)):
for ($j = 0; $j < count($arr); $j++) {
// Drop into the proper bucket based on the significant digit
$numStr = $arr[$j];
$numLen = strlen($numStr) ;
if ($numLen < $i) {
$bucketsIndex = 0;
} else {
$bucketsIndex = $numStr[$numLen - $i];
}
array_push($buckets[$bucketsIndex], $arr[$j]);
}
Or you could even (if you have enough memory) use an array to precompute the length:
$lengths = array_map ('strlen', $arr) ;

What are better ways to insert element in sorted array in PHP

I've recently send my CV to one company that was hiring PHP developers. They send me back a task to solve, to mesure if I'm experienced enough.
The task goes like that:
You have an array with 10k unique elements, sorted descendant. Write function that generates this array and next write three different functions which inserts new element into array, in the way that after insert array still will be sorted descendant. Write some code to measure speed of those functions. You can't use PHP sorting functions.
So I've wrote function to generate array and four functions to insert new element to array.
/********** Generating array (because use of range() was to simple :)): *************/
function generateSortedArray($start = 300000, $elementsNum = 10000, $dev = 30){
$arr = array();
for($i = 1; $i <= $elementsNum; $i++){
$rand = mt_rand(1, $dev);
$start -= $rand;
$arr[] = $start;
}
return $arr;
}
/********************** Four insert functions: **************************/
// for loop, and array copying
function insert1(&$arr, $elem){
if(empty($arr)){
$arr[] = $elem;
return true;
}
$c = count($arr);
$lastIndex = $c - 1;
$tmp = array();
$inserted = false;
for($i = 0; $i < $c; $i++){
if(!$inserted && $arr[$i] <= $elem){
$tmp[] = $elem;
$inserted = true;
}
$tmp[] = $arr[$i];
if($lastIndex == $i && !$inserted) $tmp[] = $elem;
}
$arr = $tmp;
return true;
}
// new element inserted at the end of array
// and moved up until correct place
function insert2(&$arr, $elem){
$c = count($arr);
array_push($arr, $elem);
for($i = $c; $i > 0; $i--){
if($arr[$i - 1] >= $arr[$i]) break;
$tmp = $arr[$i - 1];
$arr[$i - 1] = $arr[$i];
$arr[$i] = $tmp;
}
return true;
}
// binary search for correct place + array_splice() to insert element
function insert3(&$arr, $elem){
$startIndex = 0;
$stopIndex = count($arr) - 1;
$middle = 0;
while($startIndex < $stopIndex){
$middle = ceil(($stopIndex + $startIndex) / 2);
if($elem > $arr[$middle]){
$stopIndex = $middle - 1;
}else if($elem <= $arr[$middle]){
$startIndex = $middle;
}
}
$offset = $elem >= $arr[$startIndex] ? $startIndex : $startIndex + 1;
array_splice($arr, $offset, 0, array($elem));
}
// for loop to find correct place + array_splice() to insert
function insert4(&$arr, $elem){
$c = count($arr);
$inserted = false;
for($i = 0; $i < $c; $i++){
if($elem >= $arr[$i]){
array_splice($arr, $i, 0, array($elem));
$inserted = true;
break;
}
}
if(!$inserted) $arr[] = $elem;
return true;
}
/*********************** Speed tests: *************************/
// check if array is sorted descending
function checkIfArrayCorrect($arr, $expectedCount = null){
$c = count($arr);
if(isset($expectedCount) && $c != $expectedCount) return false;
$correct = true;
for($i = 0; $i < $c - 1; $i++){
if(!isset($arr[$i + 1]) || $arr[$i] < $arr[$i + 1]){
$correct = false;
break;
}
}
return $correct;
}
// claculates microtimetime diff
function timeDiff($startTime){
$diff = microtime(true) - $startTime;
return $diff;
}
// prints formatted execution time info
function showTime($func, $time){
printf("Execution time of %s(): %01.7f s\n", $func, $time);
}
// generated elements num
$elementsNum = 10000;
// generate starting point
$start = 300000;
// generated elements random range 1 - $dev
$dev = 50;
echo "Generating array with descending order, $elementsNum elements, begining from $start\n";
$startTime = microtime(true);
$arr = generateSortedArray($start, $elementsNum, $dev);
showTime('generateSortedArray', timeDiff($startTime));
$step = 2;
echo "Generating second array using range range(), $elementsNum elements, begining from $start, step $step\n";
$startTime = microtime(true);
$arr2 = range($start, $start - $elementsNum * $step, $step);
showTime('range', timeDiff($startTime));
echo "Checking if array is correct\n";
$startTime = microtime(true);
$sorted = checkIfArrayCorrect($arr, $elementsNum);
showTime('checkIfArrayCorrect', timeDiff($startTime));
if(!$sorted) die("Array is not in descending order!\n");
echo "Array OK\n";
$toInsert = array();
// number of elements to insert from every range
$randElementNum = 20;
// some ranges of elements to insert near begining, middle and end of generated array
// start value => end value
$ranges = array(
300000 => 280000,
160000 => 140000,
30000 => 0,
);
foreach($ranges as $from => $to){
$values = array();
echo "Generating $randElementNum random elements from range [$from - $to] to insert\n";
while(count($values) < $randElementNum){
$values[mt_rand($from, $to)] = 1;
}
$toInsert = array_merge($toInsert, array_keys($values));
}
// some elements to insert on begining and end of array
array_push($toInsert, 310000);
array_push($toInsert, -1000);
echo "Generated elements: \n";
for($i = 0; $i < count($toInsert); $i++){
if($i > 0 && $i % 5 == 0) echo "\n";
printf("%8d, ", $toInsert[$i]);
if($i == count($toInsert) - 1) echo "\n";
}
// functions to test
$toTest = array('insert1' => null, 'insert2' => null, 'insert3' => null, 'insert4' => null);
foreach($toTest as $func => &$time){
echo "\n\n================== Testing speed of $func() ======================\n\n";
$tmpArr = $arr;
$startTime = microtime(true);
for($i = 0; $i < count($toInsert); $i++){
$func($tmpArr, $toInsert[$i]);
}
$time = timeDiff($startTime, 'checkIfArraySorted');
showTime($func, $time);
echo "Checking if after using $func() array is still correct: \n";
if(!checkIfArrayCorrect($tmpArr, count($arr) + count($toInsert))){
echo "Array INCORRECT!\n\n";
}else{
echo "Array OK!\n\n";
}
echo "Few elements from begining of array:\n";
print_r(array_slice($tmpArr, 0, 5));
echo "Few elements from end of array:\n";
print_r(array_slice($tmpArr, -5));
//echo "\n================== Finished testing $func() ======================\n\n";
}
echo "\n\n================== Functions time summary ======================\n\n";
print_r($toTest);
Results can be found here: http://ideone.com/1xQ3T
Unfortunately I was rated only 13 points out of 30 for this task (don't know how it was calculated or what exactly was taken in account). I can only assume that's because there are better ways to insert new element into sorted array in PHP. I'm searching this topic for some time now but couldn't find anything good. Maby you know of better approach or some articles about that topic?
Btw on my localhost (PHP 5.3.6-13ubuntu3.6 with Suhosin-Patch, AMD Athlon(tm) II X4 620) insert2() is fastest, but on ideone (PHP 5.2.11) insert3() is fastest.
Any ideas why? I suppose that array_splice() is tuned up somehow :).
//EDIT
Yesterday I thought about it again, and figured out the better way to do inserts. If you only need sorted structure and a way to iterate over it and your primary concern is the speed of insert operation, than the best choise would be using SplMaxHeap class. In SplMaxHeap class inserts are damn fast :) I've modified my script to show how fast inserts are. Code is here: http://ideone.com/vfX98 (ideone has php 5.2 so there won't be SplMaxHeap class)
On my localhost I get results like that:
================== Functions time summary ======================
insert1() => 0.5983521938
insert2() => 0.2605950832
insert3() => 0.3288729191
insert4() => 0.3288729191
SplMaxHeap::insert() => 0.0000801086
It may just be me, but maybe they were looking for readability and maintainability as well?
I mean, you're naming your variables $arr, and $c and $middle, without even bothering to place proper documentation.
Example:
/**
* generateSortedArray() Function to generate a descending sorted array
*
* #param int $start Beginning with this number
* #param int $elementsNum Number of elements in array
* #param int $dev Maximum difference between elements
* #return array Sorted descending array.
*/
function generateSortedArray($start = 300000, $elementsNum = 10000, $dev = 30) {
$arr = array(); #Variable definition
for ($i = 1; $i <= $elementsNum; $i++) {
$rand = mt_rand(1, $dev); #Generate a random number
$start -= $rand; #Substract from initial value
$arr[] = $start; #Push to array
}
return $arr;
}

Categories