PHP - Optimization - Levenshtein distance with prioritization - php
I am trying to implement the levenshtein algorithm with a little addon. I want to prioritize values that have consecutive matching letters. I've tried implementing my own form of it using the code below:
function levenshtein_rating($string1, $string2) {
$GLOBALS['lvn_memo'] = array();
return lev($string1, 0, strlen($string1), $string2, 0, strlen($string2));
}
function lev($s1, $s1x, $s1l, $s2, $s2x, $s2l, $cons = 0) {
$key = $s1x . "," . $s1l . "," . $s2x . "," . $s2l;
if (isset($GLOBALS['lvn_memo'][$key])) return $GLOBALS['lvn_memo'][$key];
if ($s1l == 0) return $s2l;
if ($s2l == 0) return $s1l;
$cost = 0;
if ($s1[$s1x] != $s2[$s2x]) $cost = 1;
else $cons -= 0.1;
$dist = min(
(lev($s1, $s1x + 1, $s1l - 1, $s2, $s2x, $s2l, $cons) + 1),
(lev($s1, $s1x, $s1l, $s2, $s2x + 1, $s2l - 1, $cons) + 1),
(lev($s1, $s1x + 1, $s1l - 1, $s2, $s2x + 1, $s2l - 1, $cons) + $cost)
);
$GLOBALS['lvn_memo'][$key] = $dist + $cons;
return $dist + $cons;
}
You should note the $cons -= 0.1; is the part where I am adding a value to prioritize consecutive values. This formula will be checking against a large database of strings. (As high as 20,000 - 50,000) I've done a benchmark test with PHP's built in levenshtein
Message Time Change Memory
PHP N/A 9300128
End PHP 1ms 9300864
End Mine 20ms 9310736
Array
(
[0] => 3
[1] => 3
[2] => 0
)
Array
(
[0] => 2.5
[1] => 1.9
[2] => -1.5
)
Benchmark Test Code:
$string1 = "kitten";
$string2 = "sitter";
$string3 = "sitting";
$log = new Logger("PHP");
$distances = array();
$distances[] = levenshtein($string1, $string3);
$distances[] = levenshtein($string2, $string3);
$distances[] = levenshtein($string3, $string3);
$log->log("End PHP");
$distances2 = array();
$distances2[] = levenshtein_rating($string1, $string3);
$distances2[] = levenshtein_rating($string2, $string3);
$distances2[] = levenshtein_rating($string3, $string3);
$log->log("End Mine");
echo $log->status();
echo "<pre>" . print_r($distances, true) . "</pre>";
echo "<pre>" . print_r($distances2, true) . "</pre>";
I recognize that PHP's built in function will probably always be faster than mine by nature. But I am wondering if there is a way to speed mine up?
So the question: Is there a way to speed this up? My alternative here is to run levenshtein and then search through the highest X results of that and prioritize them additionally.
Based on Leigh's comment, copying PHP's built in form of Levenhstein lowered the time down to 3ms. (EDIT: Posted the version with consecutive character deductions. This may need tweaked, by appears to work.)
function levenshtein_rating($s1, $s2, $cons = 0, $cost_ins = 1, $cost_rep = 1, $cost_del = 1) {
$s1l = strlen($s1);
$s2l = strlen($s2);
if ($s1l == 0) return $s2l;
if ($s2l == 0) return $s1l;
$p1 = array();
$p2 = array();
for ($i2 = 0; $i2 <= $s2l; ++$i2) {
$p1[$i2] = $i2 * $cost_ins;
}
$cons = 0;
$cons_count = 0;
$cln = 0;
$tbl = $s1;
$lst = false;
for ($i1 = 0; $i1 < $s1l; ++$i1) {
$p2[0] = $p1[0] + $cost_del;
$srch = true;
for($i2 = 0; $i2 < $s2l; ++ $i2) {
$c0 = $p1[$i2] + (($s1[$i1] == $s2[$i2]) ? 0 : $cost_rep);
if ($srch && $s2[$i2] == $tbl[$i1]) {
$tbl[$i1] = "\0";
$srch = false;
$cln += ($cln == 0) ? 1 : $cln * 1;
}
$c1 = $p1[$i2 + 1] + $cost_del;
if ($c1 < $c0) $c0 = $c1;
$c2 = $p2[$i2] + $cost_ins;
if ($c2 < $c0) $c0 = $c2;
$p2[$i2 + 1] = $c0;
}
if (!$srch && $lst) {
$cons_count += $cln;
$cln = 0;
}
$lst = $srch;
$tmp = $p1;
$p1 = $p2;
$p2 = $tmp;
}
$cons_count += $cln;
$cons = -1 * ($cons_count * 0.1);
return $p1[$s2l] + $cons;
}
I think the major slowdown in your function is the fact that it's recursive.
As I've said in my comments, PHP function calls are notoriously heavy work for the engine.
PHP itself implements levenshtein as a loop, keeping a running total of the cost incurred for inserts, replacements and deletes.
I'm sure if you converted your code to a loop as well you'd see some massive performance increases.
I don't know exactly what your code is doing, but I have ported the native C code to PHP to give you a starting point.
define('LEVENSHTEIN_MAX_LENGTH', 12);
function lev2($s1, $s2, $cost_ins = 1, $cost_rep = 1, $cost_del = 1)
{
$l1 = strlen($s1);
$l2 = strlen($s2);
if ($l1 == 0) {
return $l2 * $cost_ins;
}
if ($l2 == 0) {
return $l1 * $cost_del;
}
if (($l1 > LEVENSHTEIN_MAX_LENGTH) || ($l2 > LEVENSHTEIN_MAX_LENGTH)) {
return -1;
}
$p1 = array();
$p2 = array();
for ($i2 = 0; $i2 <= $l2; $i2++) {
$p1[$i2] = $i2 * $cost_ins;
}
for ($i1 = 0; $i1 < $l1; $i1++) {
$p2[0] = $p1[0] + $cost_del;
for ($i2 = 0; $i2 < $l2; $i2++) {
$c0 = $p1[$i2] + (($s1[$i1] == $s2[$i2]) ? 0 : $cost_rep);
$c1 = $p1[$i2 + 1] + $cost_del;
if ($c1 < $c0) {
$c0 = $c1;
}
$c2 = $p2[$i2] + $cost_ins;
if ($c2 < $c0) {
$c0 = $c2;
}
$p2[$i2 + 1] = $c0;
}
$tmp = $p1;
$p1 = $p2;
$p2 = $tmp;
}
return $p1[$l2];
}
I did a quick benchmark comparing yours, mine, and PHPs internal functions, 100,000 iterations each, time is in seconds.
float(12.954766988754)
float(2.4660499095917)
float(0.14857912063599)
Obviously it hasn't got your tweaks in it yet, but I'm sure they wont slow it down that much.
If you really need more of a speed boost, once you have worked out how to change this function, it should be easy enough to port your changes back into C, make a copy of PHPs function definitions, and implement your own native C version of your modified function.
There's lots of tutorials out there on how to make PHP extensions, so you shouldn't have that much difficulty if you decide to go down that route.
Edit:
Was looking at ways to improve it further, I noticed
$c0 = $p1[$i2] + (($s1[$i1] == $s2[$i2]) ? 0 : $cost_rep);
$c1 = $p1[$i2 + 1] + $cost_del;
if ($c1 < $c0) {
$c0 = $c1;
}
$c2 = $p2[$i2] + $cost_ins;
if ($c2 < $c0) {
$c0 = $c2;
}
Is the same as
$c0 = min(
$p1[$i2 + 1] + $cost_del,
$p1[$i2] + (($s1[$i1] == $s2[$i2]) ? 0 : $cost_rep),
$c2 = $p2[$i2] + $cost_ins
);
Which I think directly relates to the min block in your code. However, this slows down the code quite significantly. (I guess its the overhead of the extra function call)
Benchmarks with the min() block as the second timing.
float(2.484846830368)
float(3.6055288314819)
You were right about the second $cost_ins not belonging - copy/paste fail on my part.
Related
How translate a neural network from C to PHP
I'm trying to translate this algorithm from C language to PHP (for study) This is an example of a perceptron. I copied the example written in c and I'm trying to translate it into PHP. Currently I wrote this code, what am I wrong? As output I only know 101 iterations with result always 1. This is the C program: #include <stdio.h> #include <stdlib.h> #include <math.h> #include <time.h> #define LEARNING_RATE 0.1 #define MAX_ITERATION 100 float randomFloat() { return (float)rand() / (float)RAND_MAX; } int calculateOutput(float weights[], float x, float y) { float sum = x * weights[0] + y * weights[1] + weights[2]; return (sum >= 0) ? 1 : -1; } int main(int argc, char *argv[]) { srand(time(NULL)); float x[208], y[208], weights[3], localError, globalError; int outputs[208], patternCount, i, p, iteration, output; FILE *fp; if ((fp = fopen("test1.txt", "r")) == NULL) { printf("Cannot open file.\n"); exit(1); } i = 0; while (fscanf(fp, "%f %f %d", &x[i], &y[i], &outputs[i]) != EOF) { if (outputs[i] == 0) { outputs[i] = -1; } i++; } patternCount = i; weights[0] = randomFloat(); weights[1] = randomFloat(); weights[2] = randomFloat(); iteration = 0; do { iteration++; globalError = 0; for (p = 0; p < patternCount; p++) { output = calculateOutput(weights, x[p], y[p]); localError = outputs[p] - output; weights[0] += LEARNING_RATE * localError * x[p]; weights[1] += LEARNING_RATE * localError * y[p]; weights[2] += LEARNING_RATE * localError; globalError += (localError*localError); } /* Root Mean Squared Error */ printf("Iteration %d : RMSE = %.4f\n", iteration, sqrt(globalError/patternCount)); } while (globalError != 0 && iteration<=MAX_ITERATION); printf("\nDecision boundary (line) equation: %.2f*x + %.2f*y + %.2f = 0\n", weights[0], weights[1], weights[2]); return 0; } and this is the code that I wrote <?php define("LEARNING_RATE", 0.1); define("MAX_ITERATION", 100); function randomFloat(){ return (float) mt_rand() / mt_getrandmax(); } function calculateOutput($weights, $x, $y){ $sum = (float) $x * $weights[0] + $y * $weights[1] + $weights[2]; return ($sum >= 0) ? 1 : -1; } srand(time()); $i = 0; $ars = explode("\n",file_get_contents('https://raw.githubusercontent.com/RichardKnop/ansi-c-perceptron/master/test1.txt')); foreach($ars as $ar){ $temp = explode("\t", $ar); $x[$i] = (float) $temp[0]; $y[$i] = (float) $temp[1]; $output[$i] = (int) $temp[2]; if($output[$i] == 0) $output[$i] = -1; $i++; } $patternCount = $i; $weights[0] = randomFloat(); $weights[1] = randomFloat(); $weights[2] = randomFloat(); $iteration = 0; do{ $iteration++; $globalError = 0; for ($p = 0; $p < $patternCount; $p++) { $output = calculateOutput($weights, $x[$p], $y[$p]); $localError = $outputs[$p] - $output; $weights[0] += LEARNING_RATE * $localError * $x[$p]; $weights[1] += LEARNING_RATE * $localError * $y[$p]; $weights[2] += LEARNING_RATE * $localError; $globalError += ($localError*$localError); } $r .= "Iteration $iteration : RMSE = " . sqrt($globalError/$patternCount)."<br>"; }while($globalError != 0 && $iteration<=MAX_ITERATION); echo $r; echo "<br><hr><br>"; echo "Decision boundary (line) equation: ".$weights[0]."*x + ".$weights[1]."*y + ".$weights[2]." = 0<br>"; it's practically identical, but why does it not work?
$ars = explode("\n",file_get_contents('…')); Since the file ends with a \n, this yields an empty string as the last array value, which disrupts the foreach($ars as $ar) loop. To read the file into an array, simply use: $ars = file('…'); In the foreach($ars as $ar) loop, you used the wrong name $output[$i] instead of $outputs[$i]. $r .= "Iteration $iteration : RMSE = " . sqrt($globalError/$patternCount)."<br>"; }while($globalError != 0 && $iteration<=MAX_ITERATION); echo $r; You didn't initialize $r. Instead of the above, you can use: echo "Iteration $iteration : RMSE = " . sqrt($globalError/$patternCount)."<br>"; } while ($globalError != 0 && $iteration<=MAX_ITERATION);
Basic perceptron for AND gate in PHP, am I doing it right? Weird results
I'd like to learn about neural nets starting with the very basic perceptron algorithm. So I've implemented one in PHP and I'm getting weird results after training it. All the 4 possible input combinations return either wrong or correct results (more often the wrong ones). 1) Is there something wrong with my implementation or the results I'm getting are normal? 2) Can this kind of implementation work with more than 2 inputs? 3) What would be the next (easiest) step in learning neural nets after this? Maybe adding more neurons, changing the activation function, or ...? P.S. I'm pretty bad at math and don't necessarily understand the math behind perceptron 100%, at least not the training part. Perceptron Class <?php namespace Perceptron; class Perceptron { // Number of inputs protected $n; protected $weights = []; protected $bias; public function __construct(int $n) { $this->n = $n; // Generate random weights for each input for ($i = 0; $i < $n; $i++) { $w = mt_rand(-100, 100) / 100; array_push($this->weights, $w); } // Generate a random bias $this->bias = mt_rand(-100, 100) / 100; } public function sum(array $inputs) { $sum = 0; for ($i = 0; $i < $this->n; $i++) { $sum += ($inputs[$i] * $this->weights[$i]); } return $sum + $this->bias; } public function activationFunction(float $sum) { return $sum < 0.0 ? 0 : 1; } public function predict(array $inputs) { $sum = $this->sum($inputs); return $this->activationFunction($sum); } public function train(array $trainingSet, float $learningRate) { foreach ($trainingSet as $row) { $inputs = array_slice($row, 0, $this->n); $correctOutput = $row[$this->n]; $output = $this->predict($inputs); $error = $correctOutput - $output; // Adjusting the weights $this->weights[0] = $this->weights[0] + ($learningRate * $error); for ($i = 0; $i < $this->n - 1; $i++) { $this->weights[$i + 1] = $this->weights[$i] + ($learningRate * $inputs[$i] * $error); } } // Adjusting the bias $this->bias += ($learningRate * $error); } } Main File <?php require_once 'vendor/autoload.php'; use Perceptron\Perceptron; // Create a new perceptron with 2 inputs $perceptron = new Perceptron(2); // Test the perceptron echo "Before training:\n"; $output = $perceptron->predict([0, 0]); echo "{$output} - " . ($output == 0 ? 'correct' : 'nope') . "\n"; $output = $perceptron->predict([0, 1]); echo "{$output} - " . ($output == 0 ? 'correct' : 'nope') . "\n"; $output = $perceptron->predict([1, 0]); echo "{$output} - " . ($output == 0 ? 'correct' : 'nope') . "\n"; $output = $perceptron->predict([1, 1]); echo "{$output} - " . ($output == 1 ? 'correct' : 'nope') . "\n"; // Train the perceptron $trainingSet = [ // The 3rd column is the correct output [0, 0, 0], [0, 1, 0], [1, 0, 0], [1, 1, 1], ]; for ($i = 0; $i < 1000; $i++) { $perceptron->train($trainingSet, 0.1); } // Test the perceptron again - now the results should be correct echo "\nAfter training:\n"; $output = $perceptron->predict([0, 0]); echo "{$output} - " . ($output == 0 ? 'correct' : 'nope') . "\n"; $output = $perceptron->predict([0, 1]); echo "{$output} - " . ($output == 0 ? 'correct' : 'nope') . "\n"; $output = $perceptron->predict([1, 0]); echo "{$output} - " . ($output == 0 ? 'correct' : 'nope') . "\n"; $output = $perceptron->predict([1, 1]); echo "{$output} - " . ($output == 1 ? 'correct' : 'nope') . "\n";
I must thank you for posting this question, I have wanted a chance to dive a little deeper into neural networks. Anyway, down to business. After tinkering around and verbose logging what all is happening, it ended up only requiring 1 character change to work as intended: public function sum(array $inputs) { ... //instead of multiplying the input by the weight, we should be adding the weight $sum += ($inputs[$i] + $this->weights[$i]); ... } With that change, 1000 iterations of training ends up being overkill. One bit of the code was confusing, different setting of weights: public function train(array $trainingSet, float $learningRate) { foreach ($trainingSet as $row) { ... $this->weights[0] = $this->weights[0] + ($learningRate * $error); for ($i = 0; $i < $this->n - 1; $i++) { $this->weights[$i + 1] = $this->weights[$i] + ($learningRate * $inputs[$i] * $error); } } I don't necessarily understand why you chose to do it this way. My unexperienced eye would think that the following would work as well. for ($i = 0; $i < $this->n; $i++) { $this->weight[$i] += $learningRate * $error; }
Found my silly mistake, I wasn't adjusting the bias for each row of a training set as I accidentally put it outside the foreach loop. This is what the train() method should look like: public function train(array $trainingSet, float $learningRate) { foreach ($trainingSet as $row) { $inputs = array_slice($row, 0, $this->n); $correctOutput = $row[$this->n]; $output = $this->predict($inputs); $error = $correctOutput - $output; // Adjusting the weights for ($i = 0; $i < $this->n; $i++) { $this->weights[$i] += ($learningRate * $inputs[$i] * $error); } // Adjusting the bias $this->bias += ($learningRate * $error); } } Now I get the correct results after training each time I run the script. Just 100 epochs of training is enough.
Convert VB6 code to PHP
I want to convert Visual Basic 6 Code to PHP Code. I am new to PHP please help me to convert my VB6 Code to PHP. So far I tried to convert this into php code when I tried the code there is an error in the " CryptRC4 = CryptRC4 & Chr$((pvCryptXor(baS((CLng(baS(li)) + baS(lJ)) Mod 256), Asc(Mid$(sText, lIdx, 1))))); part and also I don't know how to proceed to the sub functions. Please see the codes below. The vb code is used to encrypt strings. I want to convert it to php format. PHP Code <?php function CryptRC4($sText,$sKey){ $baS = array(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15, 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47, 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63, 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79, 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95, 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111, 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127, 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255); $baK = array(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15, 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47, 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63, 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79, 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95, 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111, 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127, 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255); $bytSwap = 0; $li = 0; $lJ = 0; $lIdx = 0; for( $lIdx = 0; $lIdx < 256; $lIdx++){ $baS[$lIdx] = $lIdx; $baK[$lIdx] = ord(substr($sKey, 1 + ($lIdx % strlen($sKey)), 1)); } for($li = 0; $li < 256; $li++){ $lJ = ($baS[$li] + $baK[$li]) % 256; $bytSwap = $baS[$li]; $baS[$li] = $baS[$lJ]; $baS[$lJ] = $bytSwap; } $li = 0; $lJ = 0; $data_str = ""; for($lIdx = 0; $lIdx < strlen($sText); $lIdx++){ $li = ($li + 1) % 256; $lJ = ($lJ + $baS[$li]) % 256; $bytSwap = $baS[$li]; $baS[$li] = $baS[$lJ]; $baS[$lJ] = $bytSwap; #echo chr((pvCryptXor($baS[(round(($baS[$li]) + $baS[$lJ])) % 256], ord(substr($sText, $lIdx, 1))))); $data_str .= chr((pvCryptXor($baS[(round(($baS[$li]) + $baS[$lJ])) % 256], ord(substr($sText, $lIdx, 1))))); } echo $data_str; } function pvCryptXor($li, $lJ){ if($li = $lJ){ $pcx = $lJ; } else { $pcx = $li Xor $lJ; } return $pcx; } unction ToHexDump($sText) { $lIdx; for($lIdx = 1; $lIdx < strlen($sText); $lIdx++){ $thd .= Right$("0" & Hex(Asc(Mid(sText, lIdx, 1))), 2) echo $thd; } return $thd; } FromHexDump("events"); function FromHexDump($sText) { $fhd = ""; for($lIdx = 0; $lIdx < strlen($sText); $lIdx++){ $fhd .= chr(CLng("&H" & Mid(sText, lIdx, 2))); } return $fhd; } ?> VB Code: Public Function CryptRC4(sText As String, sKey As String) As String On Error Resume Next Dim baS(0 To 255) As Byte Dim baK(0 To 255) As Byte Dim bytSwap As Byte Dim li As Long Dim lJ As Long Dim lIdx As Long For lIdx = 0 To 255 baS(lIdx) = lIdx baK(lIdx) = Asc(Mid$(sKey, 1 + (lIdx Mod Len(sKey)), 1)) Next For li = 0 To 255 lJ = (lJ + baS(li) + baK(li)) Mod 256 bytSwap = baS(li) baS(li) = baS(lJ) baS(lJ) = bytSwap Next li = 0 lJ = 0 For lIdx = 1 To Len(sText) li = (li + 1) Mod 256 lJ = (lJ + baS(li)) Mod 256 bytSwap = baS(li) baS(li) = baS(lJ) baS(lJ) = bytSwap CryptRC4 = CryptRC4 & Chr$((pvCryptXor(baS((CLng(baS(li)) + baS(lJ)) Mod 256), Asc(Mid$(sText, lIdx, 1))))) Next End Function Private Function pvCryptXor(ByVal li As Long, ByVal lJ As Long) As Long On Error Resume Next If li = lJ Then pvCryptXor = lJ Else pvCryptXor = li Xor lJ End If End Function Public Function ToHexDump(sText As String) As String On Error Resume Next Dim lIdx As Long For lIdx = 1 To Len(sText) ToHexDump = ToHexDump & Right$("0" & Hex(Asc(Mid(sText, lIdx, 1))), 2) Next End Function Public Function FromHexDump(sText As String) As String On Error Resume Next Dim lIdx As Long For lIdx = 1 To Len(sText) Step 2 FromHexDump = FromHexDump & Chr$(CLng("&H" & Mid(sText, lIdx, 2))) Next End Function
I revised your updated code and it seems you only had a few minor errors in it, look and my changes: I guess you can use the build in PHP function hex2bin and bin2hex instead fo you own hex conversion. function CryptRC4($sText,$sKey){ $baS = range(0, 255); // you can use range instead of your manual arrays $baK = range(0, 255); $bytSwap = 0; $li = 0; $lJ = 0; $lIdx = 0; for( $lIdx = 0; $lIdx < 256; $lIdx++){ $baS[$lIdx] = $lIdx; $baK[$lIdx] = ord(substr($sKey, 1 + ($lIdx % strlen($sKey)), 1)); } for($li = 0; $li < 256; $li++){ $lJ = ($baS[$li] + $baK[$li]) % 256; $bytSwap = $baS[$li]; $baS[$li] = $baS[$lJ]; $baS[$lJ] = $bytSwap; } $li = 0; $lJ = 0; $data_str = ""; for($lIdx = 0; $lIdx < strlen($sText); $lIdx++){ $li = ($li + 1) % 256; $lJ = ($lJ + $baS[$li]) % 256; $bytSwap = $baS[$li]; $baS[$li] = $baS[$lJ]; $baS[$lJ] = $bytSwap; #echo chr((pvCryptXor($baS[(round(($baS[$li]) + $baS[$lJ])) % 256], ord(substr($sText, $lIdx, 1))))); $data_str .= chr((pvCryptXor($baS[(round(($baS[$li]) + $baS[$lJ])) % 256], ord(substr($sText, $lIdx, 1))))); } return $data_str; // changed from echo to return } function pvCryptXor($li, $lJ){ if($li == $lJ){ // you had an error here, use == to compare instead of a single = $pcx = $lJ; } else { $pcx = $li ^ $lJ; // XOR function in PHP is the ^ operator } return $pcx; } $str_hex = bin2hex("events"); $str_enc = CryptRC4($str_hex,"password"); $str_dec = hex2bin(CryptRC4($str_enc,"password")); echo $str_hex . PHP_EOL . $str_enc . PHP_EOL . $str_dec; OUTPUT: 6576656e7473 '�����~i�� events So it seems to me as it's actually encoding and decoding correctly!?
It seems the original VB6 implementation of CryptRC4 function is from my answer to "VB6 encrypt text using password" question on SO. So let me try answering your Q with this short php implementation of all public functions in the VB6 snippet: function CryptRC4($text, $key) { return openssl_encrypt($text, "RC4-40", $key, 1 | 2); } function ToHexDump($text) { return strtoupper(bin2hex($text)); } function FromHexDump($text) { return hex2bin($text); } You can excercise these one-liners with something like this: $text = "a message here"; $password = "password"; $encr = ToHexDump(CryptRC4($text, $password)); $decr = CryptRC4(FromHexDump($encr), $password); echo $text . PHP_EOL . $encr . PHP_EOL . $decr;
As it's intended for passwords, you can save yourself a lot of hassle. PHP has got built in functions (version 5.5 and newer) that are designed for dealing with the hashing of passwords and for verifying hashed passwords against the password submitted by a user. Have a read through the PHP relevant PHP manual pages http://php.net/manual/en/book.password.php
How do I use Holt-Winters Seasonal Dampened Method to compute a two-month sales projection in PHP?
Holt-Winters is introduced here: http://en.wikipedia.org/wiki/Holt-Winters The Seasonal Dampened version of it is discussed here (scroll down the page): http://otexts.com/fpp/7/5/ In a nutshell, it basically looks at 3 things: long-term trend short-term trend seasonal trend It also doesn't average those together, because really what you need is weighted averaging, where seasonal and short-term are more significant than long-term trend, naturally, with financial data trends. Given $anYear1 and $anYear2, how do I apply the Holt-Winters Seasonal Dampened Method to forecast 2 more months past the end of $anYear2? Assume $anYear1 is an array of 12 numbers. Assume $anYear2 is an array of a range of 0 to 12 numbers. So, I can fill it with random data like so: <?php $anYear1 = array(); $anYear2 = array(); $nStop = 10; // so we need 11 and 12 of the year for ($i = 1; $i <= 12; $i++) { $anYear1[$i] = rand(200,500); if ($i <= $nStop) { // give it a natural lift like real financial data $anYear2[$i] = rand(400,700); } } $nSeasonRange = 4; // 4 months in a business quarter Therefore, I want to create a function like so: function forecastHoltWinters($anYear1, $anYear2, $nSeasonRange = 4) { /////////////////// // DO MAGIC HERE // /////////////////// // an array with 2 numbers, indicating 2 months forward from end of $anYear2 return $anForecast; } $anForecast = forecastHoltWinters($anYear1, $anYear2, $nSeasonRange); echo "YEAR 1\n"; print_r($anYear1); echo "\n\nYEAR 2\n" print_r($anYear2); echo "\n\nTWO MONTHS FORECAST\n"; print_r($anForecast); Note: I have found a Github example here, but it doesn't show how to do a projection. It is also discussed here.
I found a way to adapt Ian Barber's function to do what I needed. <?php error_reporting(E_ALL); ini_set('display_errors','On'); $anYear1 = array(); $anYear2 = array(); $nStop = 10; for($i = 1; $i <= 12; $i++) { $anYear1[$i] = rand(100,400); if ($i <= $nStop) { $anYear2[$i+12] = rand(200,600); } } print_r($anYear1); print_r($anYear2); $anData = array_merge($anYear1,$anYear2); print_r(forecastHoltWinters($anData)); function forecastHoltWinters($anData, $nForecast = 2, $nSeasonLength = 4, $nAlpha = 0.2, $nBeta = 0.01, $nGamma = 0.01, $nDevGamma = 0.1) { // Calculate an initial trend level $nTrend1 = 0; for($i = 0; $i < $nSeasonLength; $i++) { $nTrend1 += $anData[$i]; } $nTrend1 /= $nSeasonLength; $nTrend2 = 0; for($i = $nSeasonLength; $i < 2*$nSeasonLength; $i++) { $nTrend2 += $anData[$i]; } $nTrend2 /= $nSeasonLength; $nInitialTrend = ($nTrend2 - $nTrend1) / $nSeasonLength; // Take the first value as the initial level $nInitialLevel = $anData[0]; // Build index $anIndex = array(); foreach($anData as $nKey => $nVal) { $anIndex[$nKey] = $nVal / ($nInitialLevel + ($nKey + 1) * $nInitialTrend); } // Build season buffer $anSeason = array_fill(0, count($anData), 0); for($i = 0; $i < $nSeasonLength; $i++) { $anSeason[$i] = ($anIndex[$i] + $anIndex[$i+$nSeasonLength]) / 2; } // Normalise season $nSeasonFactor = $nSeasonLength / array_sum($anSeason); foreach($anSeason as $nKey => $nVal) { $anSeason[$nKey] *= $nSeasonFactor; } $anHoltWinters = array(); $anDeviations = array(); $nAlphaLevel = $nInitialLevel; $nBetaTrend = $nInitialTrend; foreach($anData as $nKey => $nVal) { $nTempLevel = $nAlphaLevel; $nTempTrend = $nBetaTrend; $nAlphaLevel = $nAlpha * $nVal / $anSeason[$nKey] + (1.0 - $nAlpha) * ($nTempLevel + $nTempTrend); $nBetaTrend = $nBeta * ($nAlphaLevel - $nTempLevel) + ( 1.0 - $nBeta ) * $nTempTrend; $anSeason[$nKey + $nSeasonLength] = $nGamma * $nVal / $nAlphaLevel + (1.0 - $nGamma) * $anSeason[$nKey]; $anHoltWinters[$nKey] = ($nAlphaLevel + $nBetaTrend * ($nKey + 1)) * $anSeason[$nKey]; $anDeviations[$nKey] = $nDevGamma * abs($nVal - $anHoltWinters[$nKey]) + (1-$nDevGamma) * (isset($anDeviations[$nKey - $nSeasonLength]) ? $anDeviations[$nKey - $nSeasonLength] : 0); } $anForecast = array(); $nLast = end($anData); for($i = 1; $i <= $nForecast; $i++) { $nComputed = round($nAlphaLevel + $nBetaTrend * $anSeason[$nKey + $i]); if ($nComputed < 0) { // wildly off due to outliers $nComputed = $nLast; } $anForecast[] = $nComputed; } return $anForecast; }
Generate alphanumeric unique numbers
I want to generate alphanumeric unique numbers but the format should be like this that should be starts from AA001 to AA999 after that AB001 to AB999 .... BA001 to BA999 end with ZZ999. if i give the input is 1 = result AA001 999 = result AA999 1000 = result AB001 any one can help this ?
Complete solution (see it running): function formatNum1000($num) { $tail = $num % 1000; $head = (int)($num / 1000); $char1 = chr(ord('A') + (int)($head / 26)); $char2 = chr(ord('A') + ($head % 26)); return sprintf('%s%s%03d', $char1, $char2, $tail); } function formatNum999($num) { $tail = (($num - 1 ) % 999) + 1; $head = (int)(($num - $tail) / 999); $char1 = chr(ord('A') + (int)($head / 26)); $char2 = chr(ord('A') + ($head % 26)); return sprintf('%s%s%03d', $char1, $char2, $tail); } $ns = array(1, 500, 999, 1000, 1998, 1999, 2000, 25974, 25975, 25999, 26000, 675324, 675999); foreach($ns as $n) { $formatted1000 = formatNum1000($n); $formatted999 = formatNum999 ($n); echo "Num: $n => $formatted1000 / $formatted999\n"; } Note: you need to make sure that the input number is within the valid range (0...675999 when including 000-numbers, 1...675324 otherwise) Note: answer revised, missed the point earlier that 000 is not allowed
How about: $start = 'AA997'; for($i = 0; $i < 5; $i++) { $start++; if (substr($start, 2) == '000') continue; echo $start,"\n"; } output: AA998 AA999 AB001 AB002