How translate a neural network from C to PHP - 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);
Related
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
Increment individual IPs from IPv6 string (php)
What is a simple, elegant way to list the first x number of IPv6 IPs from a IPv6 string. For example, listIPs("2600:f333:10:c000::0", 4) echos 2600:f333:10:c000::1 2600:f333:10:c000::2 2600:f333:10:c000::3 2600:f333:10:c000::4 Here's a sample of code that may have worked for IPv4, as it converted to int: $input = "2600:f333:10:c000::/51"; $max = 4; list($block, $cidr) = explode("/", $input); $first = inet_pton( $block ); echo inet_ntop($first) . "\n"; for ($i = 1; $i < $max; $i++) { //todo: die if it has exceeded block size based on $cidr echo inet_ntop($first + $i) . "\n"; //doesn't work, packed binary? }
Here's an example program written in C (since I don't know C++). It's fairly fast, but I'm not really happy with it. Maybe someone can help me improve it. Edit: Obviously, I wrote this before it was turned into a PHP-only question. Turning this into PHP is left as an exercise to the reader (ew). #include <arpa/inet.h> #include <string.h> #include <stdlib.h> #include <errno.h> #include <stdio.h> #include <ctype.h> /* * Syntax: ./ipv6_list <ip>/<cidr-prefix> */ int main(int argc, char **argv) { uint8_t start[16]; uint8_t address[16]; uint8_t mask[16] = { 0 }; uint8_t prefix = 128; char *prefix_location; int i; /* This is the octet that, when changed, will result in <IP> & <mask> != <start IP> */ int mask_check_octet = 0; if(argc != 2) return 1; /* Find prefix */ if((prefix_location = strstr(argv[1], "/")) != NULL) { char *prefix_search = prefix_location + 1; char *prefix_remaining; long prefix_test; if(!isdigit(*prefix_search)) return 2; errno = 0; prefix_test = strtol(prefix_search, &prefix_remaining, 10); if(errno == ERANGE || prefix_test < 0 || prefix_test > 128 || strcmp(prefix_remaining, "") != 0) return 2; prefix = (uint8_t)prefix_test; *prefix_location = '\0'; /* So we can just pass argv[1] into inet_pton(3) */ } /* Convert prefix into mask */ for(i = 0; i < 16; i++) { if(prefix == 0) break; mask_check_octet = i; if(prefix < 8) { mask[i] = ~((1 << (8 - prefix)) - 1); break; } else mask[i] = UINT8_MAX; prefix -= 8; } /* Find address */ if(inet_pton(AF_INET6, argv[1], start) != 1) return 3; /* Start at the beginning of the network */ for(i = 0; i < 16; i++) { start[i] &= mask[i]; address[i] = start[i]; } /* Iterate */ while((address[mask_check_octet] & mask[mask_check_octet]) == start[mask_check_octet]) { char address_str[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, address, address_str, sizeof(address_str)); printf("%s\n", address_str); /* Add one to the address */ for(i = 15; i >= 0; i--) { if(address[i] != UINT8_MAX) break; } address[i]++; for(i++; i < 16; i++) address[i] = 0; }; return 0; } You can use standard shell commands to limit its output (or just modify the while loop): nfontes#brioche:~$ ./ipv6_list '2607:fc50:0:d00::0/106' | head -n 200 2607:fc50:0:d00:: 2607:fc50:0:d00::1 2607:fc50:0:d00::2 2607:fc50:0:d00::3 2607:fc50:0:d00::4 2607:fc50:0:d00::5 2607:fc50:0:d00::6 2607:fc50:0:d00::7 2607:fc50:0:d00::8 2607:fc50:0:d00::9 2607:fc50:0:d00::a 2607:fc50:0:d00::b 2607:fc50:0:d00::c 2607:fc50:0:d00::d 2607:fc50:0:d00::e [...] 2607:fc50:0:d00::c0 2607:fc50:0:d00::c1 2607:fc50:0:d00::c2 2607:fc50:0:d00::c3 2607:fc50:0:d00::c4 2607:fc50:0:d00::c5 2607:fc50:0:d00::c6 2607:fc50:0:d00::c7
Something similar (in PHP). It takes an IPv4/IPv6 address and increments it by given value: // Takes an IPv4/IPv6 address in string format, and increments it by given value function incrementIp($ip, $increment) { $addr = inet_pton ( $ip ); for ( $i = strlen ( $addr ) - 1; $increment > 0 && $i >= 0; --$i ) { $val = ord($addr[$i]) + $increment; $increment = $val / 256; $addr[$i] = chr($val % 256); } return inet_ntop ( $addr ); }
PHP - Optimization - Levenshtein distance with prioritization
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.
Converting float decimal to fraction
I am trying to convert calculations keyed in by users with decimal results into fractions. For e.g.; 66.6666666667 into 66 2/3. Any pointers? Thanx in advance
Continued fractions can be used to find rational approximations to real numbers that are "best" in a strict sense. Here's a PHP function that finds a rational approximation to a given (positive) floating point number with a relative error less than $tolerance: <?php function float2rat($n, $tolerance = 1.e-6) { $h1=1; $h2=0; $k1=0; $k2=1; $b = 1/$n; do { $b = 1/$b; $a = floor($b); $aux = $h1; $h1 = $a*$h1+$h2; $h2 = $aux; $aux = $k1; $k1 = $a*$k1+$k2; $k2 = $aux; $b = $b-$a; } while (abs($n-$h1/$k1) > $n*$tolerance); return "$h1/$k1"; } printf("%s\n", float2rat(66.66667)); # 200/3 printf("%s\n", float2rat(sqrt(2))); # 1393/985 printf("%s\n", float2rat(0.43212)); # 748/1731 I have written more about this algorithm and why it works, and even a JavaScript demo here: https://web.archive.org/web/20180731235708/http://jonisalonen.com/2012/converting-decimal-numbers-to-ratios/
Farey fractions can be quite useful in this case. They can be used to convert any decimal into a fraction with the lowest possible denominator. Sorry - I don't have a prototype in PHP, so here's one in Python: def farey(v, lim): """No error checking on args. lim = maximum denominator. Results are (numerator, denominator); (1, 0) is 'infinity'.""" if v < 0: n, d = farey(-v, lim) return (-n, d) z = lim - lim # Get a "zero of the right type" for the denominator lower, upper = (z, z+1), (z+1, z) while True: mediant = (lower[0] + upper[0]), (lower[1] + upper[1]) if v * mediant[1] > mediant[0]: if lim < mediant[1]: return upper lower = mediant elif v * mediant[1] == mediant[0]: if lim >= mediant[1]: return mediant if lower[1] < upper[1]: return lower return upper else: if lim < mediant[1]: return lower upper = mediant
Converted Python code in answer from #APerson241 to PHP <?php function farey($v, $lim) { // No error checking on args. lim = maximum denominator. // Results are array(numerator, denominator); array(1, 0) is 'infinity'. if($v < 0) { list($n, $d) = farey(-$v, $lim); return array(-$n, $d); } $z = $lim - $lim; // Get a "zero of the right type" for the denominator list($lower, $upper) = array(array($z, $z+1), array($z+1, $z)); while(true) { $mediant = array(($lower[0] + $upper[0]), ($lower[1] + $upper[1])); if($v * $mediant[1] > $mediant[0]) { if($lim < $mediant[1]) return $upper; $lower = $mediant; } else if($v * $mediant[1] == $mediant[0]) { if($lim >= $mediant[1]) return $mediant; if($lower[1] < $upper[1]) return $lower; return $upper; } else { if($lim < $mediant[1]) return $lower; $upper = $mediant; } } } // Example use: $f = farey(66.66667, 10); echo $f[0], '/', $f[1], "\n"; # 200/3 $f = farey(sqrt(2), 1000); echo $f[0], '/', $f[1], "\n"; # 1393/985 $f = farey(0.43212, 2000); echo $f[0], '/', $f[1], "\n"; # 748/1731
Based upon #Joni's answer, here is what I used to pull out the whole number. function convert_decimal_to_fraction($decimal){ $big_fraction = float2rat($decimal); $num_array = explode('/', $big_fraction); $numerator = $num_array[0]; $denominator = $num_array[1]; $whole_number = floor( $numerator / $denominator ); $numerator = $numerator % $denominator; if($numerator == 0){ return $whole_number; }else if ($whole_number == 0){ return $numerator . '/' . $denominator; }else{ return $whole_number . ' ' . $numerator . '/' . $denominator; } } function float2rat($n, $tolerance = 1.e-6) { $h1=1; $h2=0; $k1=0; $k2=1; $b = 1/$n; do { $b = 1/$b; $a = floor($b); $aux = $h1; $h1 = $a*$h1+$h2; $h2 = $aux; $aux = $k1; $k1 = $a*$k1+$k2; $k2 = $aux; $b = $b-$a; } while (abs($n-$h1/$k1) > $n*$tolerance); return "$h1/$k1"; }
Based on #APerson's and #Jeff Monteiro's answers I've created PHP version of Farey fractions that will be simplified to whole values with fractions with lowest possible denominator: <?php class QuantityTransform { /** * #see https://stackoverflow.com/questions/14330713/converting-float-decimal-to-fraction */ public static function decimalToFraction(float $decimal, $glue = ' ', int $limes = 10): string { if (null === $decimal || $decimal < 0.001) { return ''; } $wholeNumber = (int) floor($decimal); $remainingDecimal = $decimal - $wholeNumber; [$numerator, $denominator] = self::fareyFraction($remainingDecimal, $limes); // Values rounded to 1 should be added to base value and returned without fraction part if (is_int($simplifiedFraction = $numerator / $denominator)) { $wholeNumber += $simplifiedFraction; $numerator = 0; } return (0 === $wholeNumber && 0 === $numerator) // Too small values will be returned in original format ? (string) $decimal // Otherwise let's format value - only non-0 whole value / fractions will be returned : trim(sprintf( '%s%s%s', (string) $wholeNumber ?: '', $wholeNumber > 0 ? $glue : '', 0 === $numerator ? '' : ($numerator . '/' . $denominator) )); } /** * #see https://stackoverflow.com/a/14330799/842480 * * #return int[] Numerator and Denominator values */ private static function fareyFraction(float $value, int $limes): array { if ($value < 0) { [$numerator, $denominator] = self::fareyFraction(-$value, $limes); return [-$numerator, $denominator]; } $zero = $limes - $limes; $lower = [$zero, $zero + 1]; $upper = [$zero + 1, $zero]; while (true) { $mediant = [$lower[0] + $upper[0], $lower[1] + $upper[1]]; if ($value * $mediant[1] > $mediant[0]) { if ($limes < $mediant[1]) { return $upper; } $lower = $mediant; } elseif ($value * $mediant[1] === $mediant[0]) { if ($limes >= $mediant[1]) { return $mediant; } if ($lower[1] < $upper[1]) { return $lower; } return $upper; } else { if ($limes < $mediant[1]) { return $lower; } $upper = $mediant; } } } } Then you san use it like: QuantityTransform::decimalToFraction(0.06); // 0.06 QuantityTransform::decimalToFraction(0.75); // 3/4 QuantityTransform::decimalToFraction(1.75, ' and '); // 1 and 3/4 QuantityTransform::decimalToFraction(2.33, ' and '); // 2 and 1/3 QuantityTransform::decimalToFraction(2.58, ' ', 5); // 2 3/5 QuantityTransform::decimalToFraction(2.58, ' & ', 10); // 2 & 4/7 QuantityTransform::decimalToFraction(1.97); // 2
Here is my approach to this problem. Works fine with rational numbers. function dec2fracso($dec){ //Negative number flag. $num=$dec; if($num<0){ $neg=true; }else{ $neg=false; } //Extracts 2 strings from input number $decarr=explode('.',(string)$dec); //Checks for divided by zero input. if($decarr[1]==0){ $decarr[1]=1; $fraccion[0]=$decarr[0]; $fraccion[1]=$decarr[1]; return $fraccion; } //Calculates the divisor before simplification. $long=strlen($decarr[1]); $div="1"; for($x=0;$x<$long;$x++){ $div.="0"; } //Gets the greatest common divisor. $x=(int)$decarr[1]; $y=(int)$div; $gcd=gmp_strval(gmp_gcd($x,$y)); //Calculates the result and fills the array with the correct sign. if($neg){ $fraccion[0]=((abs($decarr[0])*($y/$gcd))+($x/$gcd))*(-1); }else{ $fraccion[0]=(abs($decarr[0])*($y/$gcd))+($x/$gcd); } $fraccion[1]=($y/$gcd); return $fraccion; }
Sometimes it is necessary to treat only the decimals of a float. So I created a code that uses the function created by #Joni to present a format that is quite common in culinary recipes, at least in Brazil. So instead of using 3/2 which is the result for 1.5, using the function I created it is possible to present the value 1 1/2, and if you want, you can also add a string to concatenate the values, creating something like "1 and 1/2 ". function float2rat($n, $tolerance = 1.e-6) { $h1=1; $h2=0; $k1=0; $k2=1; $b = 1/$n; do { $b = 1/$b; $a = floor($b); $aux = $h1; $h1 = $a*$h1+$h2; $h2 = $aux; $aux = $k1; $k1 = $a*$k1+$k2; $k2 = $aux; $b = $b-$a; } while (abs($n-$h1/$k1) > $n*$tolerance); return "$h1/$k1"; } function float2fraction($float, $concat = ' '){ // ensures that the number is float, // even when the parameter is a string $float = (float)$float; if($float == 0 ){ return $float; } // when float between -1 and 1 if( $float > -1 && $float < 0 || $float < 1 && $float > 0 ){ $fraction = float2rat($float); return $fraction; } else{ // get the minor integer if( $float < 0 ){ $integer = ceil($float); } else{ $integer = floor($float); } // get the decimal $decimal = $float - $integer; if( $decimal != 0 ){ $fraction = float2rat(abs($decimal)); $fraction = $integer . $concat . $fraction; return $fraction; } else{ return $float; } } } Usage e.g: echo float2fraction(1.5); will return "1 1/2"
Welle's Wilder Accumulative Swing Index PHP calculation - Cannot return proper value
I am trying to get the accumulative swing index for an aapl stock chart. I am using this calculation for reference. http://www.barchart.com/education/std_studies.php?what=int_swing&hideheader=true#study This is what I have written so far. This should return 252.09 but I cannot get it to work. $asi[0] = -78.75 $ht = 584; // High today $lt = 574.25; // low $ct = 584.00; // close $ot = 578; // open $hy = 574; // High yesterday $ly = 565.61; $cy = 569.05; $oy = 571.67; $k = max(($hy-$ct),($ly-$ct)); $abc = array(($ht-$cy), ($lt-$cy), ($ht-$lt)); $max = max($abc); $r = 0; if($max == $abc[0]){ $r = ($ht-$cy)-.5*($lt-$cy)+.25*($cy-$oy); }elseif($max == $abc[1]){ $r = ($lt-$cy)-.5*($ht-$cy)+.25*($cy-$oy); }elseif($max == $abc[2]){ $r = ($ht-$lt)+.25*($cy-$oy); }else{ echo "Error in welles accumulative swing index"; exit; } $l = 3 //period; $val = 50 * (($cy - $ct) + .5 *($cy - $oy) + .25*($ct-$ot)) / $r * $k / $l; $asi[] = $asi[$i-1] + $val; Any help would be greatly appreciated.
I have tried to implement this index newly symbol-by-symbol and have get different result (swing: -248.7032967033 ). May be your control value wrong? That is my code: class Swing { public function calculate($high_price, $low_price, $close_price, $open_price, $t) { // (Ct-1 - Ct) $summand0 = ($close_price[$t-1] - $close_price[$t]); // 0.5(Ct-1 - Ot-1) $summand1 = 0.5 * ($close_price[$t-1] - $open_price[$t-1]); // 0.25(Ct - Ot) $summand2 = 0.25 * ($close_price[$t] - $open_price[$t]); $limit_move_default = 3.0; $r = $this->get_r_value($high_price, $low_price, $close_price, $open_price, $t); $k = $this->get_k_value($high_price, $low_price, $close_price, $t); $factor0 = 50.0 * ($summand0 + $summand1 + $summand2) / $r; $factor1 = $k / $limit_move_default; // SWING = 50 * ((Ct-1 - Ct)+ 0.5(Ct-1 - Ot-1)+ 0.25(Ct - Ot))/ R * K / M return $factor0 * $factor1; } public function get_k_value($high_price, $low_price, $close_price, $t) { // K= MAX(| Ht-Ct-1|, | Lt-Ct-1|) return max( abs($high_price[$t] - $close_price[$t-1]), abs($low_price[$t] - $close_price[$t-1])); } public function get_r_value($high_price, $low_price, $close_price, $open_price, $t) { // A. |Ht-Ct-1| $a = abs($high_price[$t] - $close_price[$t-1]); // B. |Lt-Ct-1| $b = abs($low_price[$t] - $close_price[$t-1]); // C. |Ht-Lt| $c = abs($high_price[$t] - $low_price[$t]); $max_value = max($a, $b, $c); $d = abs($high_price[$t] - $low_price[$t]); if($a == $max_value) // R= (| Ht-Ct-1|)-.5(| Lt-Ct-1|)+.25(| Ct-1-Ot-1|) return $a - 0.5 * $b + 0.25 * $d; if($b == $max_value) // R= (| Lt-Ct-1|)-.5(| Ht-Ct-1|)+.25(| Ct-1-Ot-1|) return $b - 0.5 * $a + 0.25 * $d; if($c == $max_value) // R= (| Ht-Lt|)+.25(| Ct-1-Ot-1|) return $c + 0.25 * $d; } }; $swing = new Swing(); $high_price = array(574.0, 584.0); $low_price = array(565.61, 574.25); $close_price = array(569.05, 584.0); $open_price = array(571.67, 578.0); $value = $swing->calculate($high_price, $low_price, $close_price, $open_price, 1); echo("swing: $value \n");
$d looks wrong. It should be abs($close_price[$t-1] - $open_price[$t-1]);