How to produce a unique string from a php array - php

I need a unique string from an array so that I can tell when it changes without measuring the inputs of that array. I'm trying to work out if it is computationally efficient to calculate a value rather than add code to look out for changes in the array. The array itself can have a variety of values and for future proofing I don't want to try and measure whether new values have been added to the array, I'd much rather just create some string or hash that will change if the array itself changes.
So for example:
$a = Array(
'var1' => 1,
'var2' => 2,
'var3' => 3,
);
If I was to use md5(http_build_query($a)) perhaps with an added ksort to confirm that the order of the keys haven't changed this might then produce a unique string that I can use to compare against another run of the application to evaluate whether the array has changed.
I'm looking for an alternate, possibly faster or more elegant solutions to this.

Im use md5(serialize($array)) for this. Its better, because works for multi-dimensional arrays.

Thanks for all the ideas guys.
I've tried all of them except a sha-256 which my server doesn't have installed.
Here's the results:
Average (http_build_query): 1.3954045954045E-5
Average (diff): 0.00011533766233766
Average (serialize): 1.7588411588412E-5
Average (md5): 1.6036963036966E-5
Average (implode-haval160,4): 1.5349650349649E-5
That's running the operation 1000 times and averaging the result. After refreshing a couple times I could tell that the http_build_query was the quickest. I guess my next question would be if anyone can think of any pitfalls of using this method?
Thanks
Here's my code:
class a {
static $input;
function test() {
$start = null;
$s = $e = $d = $g = $h = $i = $k = array();
self::$input = array();
for ($x = 0; $x <= 30; $x++) {
self::$input['variable_' . $x] = rand();
}
for ($x = 0; $x <= 1000; $x++) {
$start = microtime();
$c = http_build_query(self::$input);
($c == $c);
$s[] = microtime() - $start;
}
for ($x = 0; $x <= 1000; $x++) {
$start = microtime();
$c = md5(http_build_query(self::$input));
($c == $c);
$e[] = microtime() - $start;
}
for ($x = 0; $x <= 1000; $x++) {
$start = microtime();
$c = array_diff(self::$input, self::$input);
$d[] = microtime() - $start;
}
for ($x = 0; $x <= 1000; $x++) {
$start = microtime();
$c = serialize(self::$input);
($c == $c);
$g[] = microtime() - $start;
}
for ($x = 0; $x <= 1000; $x++) {
$start = microtime();
$c = hash("haval160,4", implode(',',self::$input));
($c == $c);
$h[] = microtime() - $start;
}
echo "<pre>";
//print_r($s);
echo "Average (http_build_query): " . array_sum($s) / count($s) . "<br>";
echo "Average (diff): " . array_sum($d) / count($d) . "<br>";
echo "Average (serialize): " . array_sum($g) / count($g) . "<br>";
echo "Average (md5): " . array_sum($e) / count($e). "<br>";
echo "Average (implode-haval160,4): " . array_sum($h) / count($h);
}
}
a::test();

PHP has an array_diff() function, don't know if it's of any use for you.
Otherwise, you can eventualy use the incremental hashing possibility offered by php : http://www.php.net/manual/en/function.hash-init.php by iterating over each values of the array and adding them in the incremental hash.

You could always just do
$str = implode(",", $a);
$check = hash("sha-256", $str);
Theoretically, that should detect changes in array size, data, or ordering.
Of course, you can use whatever hash you wish.

Related

Why can't I use a function to simplify my "for loops"

I build a webpage to crack simple MD5 hash of a four digit pin for fun. The method I used was basically try all combination and check against the MD5 value the user has entered. Below is the PHP code I created to accomplish the goal.
Debug Output:
<?php
$answer = "PIN not found";
if (isset($_GET['md5'])) {
$txt = 'abcdefjhig';
$time_pre = microtime(TRUE);
$value = $_GET['md5'];
$show = 15;
for ($i = 0; $i <= 9; $i++) {
$first = $i;
for ($j = 0; $j <= 9; $j++) {
$second = $j;
for ($k = 0; $k <= 9; $k++) {
$third = $k;
for ($x = 0; $x <= 9; $x++) {
$fourth = $x;
$whole = $first . $second . $third . $fourth;
$check = hash('md5', $whole);
if ($check == $value) {
$answer = $whole;
echo "The pin is $answer";
}
if ($show > 0) {
print"$check $whole \n";
$show = $show - 1;
}
}
}
}
}
echo "\n";
$time_post = microtime(TRUE);
print "Elapsed time:";
print $time_post - $time_pre;
}
?>
Notice that in the middle there are four very similar for loops,I tried to simplify this by using functions, but it just returns one four digit number which is 9999 instead of all of them.
Below is the function I created:
function construct($input){
for($i=0; $i<=9, $i++){
$input=$i;
}
return $input
}
Then I tried to call this function for four times to form all of the four digit numbers but it just gives me 9999. Can anybody help? Thanks a lot.
Try this:
for ($i=0; $i<=9999 ; $i++) {
$whole = sprintf('%04d', $i);
$check=hash('md5', $whole);
if($check==$value){
$answer=$whole;
echo "The pin is $answer";
break; //remove this if you do not want to break the loop here
}
if ($show>0) {
print"$check $whole \n";
$show=$show-1;
}
}
You're using numbers from 0 to 9999, so why just loop through those numbers? You can add zero's by using str_pad() like so:
for ($i = 0; $i <= 9999; $i++) {
$whole = str_pad($i, 4, '0', STR_PAD_LEFT);
}
Also I'd like to mention that you really should think about better formatting of your code, especially indentation

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;
}
}
?>

What is faster/more efficient as target using in_array - array() or a variable with the array in it?

In short, which is more efficient or better or faster than the other?
if (in_array($value, array('val1', 'val2', 'val3')) { ... }
or
$arr = array('val1', 'val2', 'val3');
if (in_array($value, $arr) { ... }
This code will run inside a loop, so the same array(...) declaration would happen multiple times.
Does it really matter if the array is in a variable or if it is redeclared on the fly for every in_array run inside a loop?
Speaking of speed - I prefer to use isset($arr[$key]) for the keys of the associate array instead of in_array if it is possible.
$arraySize = 10000;
$loop = 10000;
// for numbers, at first
$array = range(1, $arraySize);
$start = getMtime();
for($i = 0; $i < $loop; $i++)
{
if (in_array(rand(1, $arraySize), $array))
continue;
}
$end = getMtime();
echo ($end - $start) . '<br>';
$start = getMtime();
for($i = 0; $i < $loop; $i++)
{
if (isset($array[rand(1, $arraySize)]))
continue;
}
$end = getMtime();
echo ($end - $start) . '<br>';
// the same, but for strings
foreach($array as &$el)
$el = generateId(10);
$start = getMtime();
for($i = 0; $i < $loop; $i++)
{
if (in_array(generateId(10), $array))
continue;
}
$end = getMtime();
echo ($end - $start) . '<br>';
// now set them as keys
$array = array_flip($array);
echo 'Size of array ' . count($array) . '<br>';
$start = getMtime();
for($i = 0; $i < $loop; $i++)
{
if (isset($array[generateId(10)]))
continue;
}
$end = getMtime();
echo $end - $start;
function getMtime()
{
$mtime = microtime();
$mtime = explode(" ",$mtime);
$mtime = $mtime[1] + $mtime[0];
return $mtime;
}
function generateId($len = 10)
{
return join('', array_map(function() { return substr('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', rand(0, 61), 1); }, range(1, $len)));
}
Results:
0.69922399520874 // in_array, numbers
0.0039558410644531 // isset, numbers
3.5183579921722 // in_array, strings
Size of array 10000
0.15712094306946 // isset, strings
No repeating keys, so the size of flipped array is the same. Of course, may be I'm missing something, but I do not expect high speed from in_array function.
The first way is faster, but not as legible. However, most arrays you need to evaluate this way are going to be arrays which you've already assigned to a variable for other purposes. The latter method requires an extra assignment, so it would take slightly more memory and time.
You should create or appropriate a time tracking class, if you care how fast things are (which is definitely a good way to approach programming). Almost every new method of doing things I create, I test the speed of it, and compare that to existing methods, to make sure it's beneficial/efficient and that I'm on the right track. Create a time class and execute each scenario you want to compare some 10000+ iterations, depending on what your testing, to see which is faster. That way you'll never have to guess or get somebody else's insight on the matter.
Edit:
This may not need to be said, but I thought I should add it just in case:
Any time class in php would need the ability to track milliseconds to be relevant.

How to output the arguments of a Function in the Function itself

<?php
$l = 4000;
function flip($h, $t)
{
if ($h + $t <= 29) {
$a = mt_rand(0, 1);
if ($a == 1) {
flip($h + 1, $t);
} else {
flip($h, $t + 1);
}
} else {
echo "$h,$t" . "</br>";
}
}
$ar = array( "test");
for ($i = 0; $i <= $l; $i++) {
array_push($ar, flip(0, 0));
}
?>
My code is supposed to give the number of tails and heads upon spinnings 30 times. And this process is done 4000 times. I want to store all the results of the 4000 times in an array using the above code. But I think the way I defined by flip is wrong. I didn't know how to store the value of the arguments of flip after it is done therefore I used an echo.
When i run this code.. for some reason it is printing out all the answers like this : http://searchr.us/testing/ip.php instead of just making an array. I have pin pointed that the way I took the vfilanl values of flip wrong. Can someone tell me the difference between echo , print and how DO i use return. When i just use return(); after echo.It gives me an error.
Please clarify my doubt on how I can output these values.
You should use something like this:
<?php
function flip($h, $t)
{
if ($h + $t > 29) return $h . ',' . $t;
$a = mt_rand(0, 1);
return $a == 1 ? flip($h + 1, $t) : flip($h, $t + 1);
}
$ar = array( "test");
$l = 4000;
for ($i = 0; $i <= $l; ++$i) {
array_push($ar, flip(0, 0));
}
?>
Note that echo outputs to the client.
But you want to decide what you get when you call flip, and that is done using return.

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