This is more of a puzzle than anything. I've actually found a solution but it is so slow I thought I lost my internet connection (see below).
Here's the problem:
Let's say I have an array of numbers, like so:
$numbers_array = array(1, 2, 3, 4, 5, 6, 7, 8, 9);
Let's also say that I have a number some numbers, stored in variables like so:
$sum = 15;
$sum2 = 24;
$sum3 = 400;
I am trying to create a function that will return true if any of the numbers in $numbers_array can be added together (each only used once) to form the sums:
function is_summable($array_of_nums, $sum_to_check) {
//What to put here?
}
var_dump(is_summable($numbers_array, $sum));
var_dump(is_summable($numbers_array, $sum2));
var_dump(is_summable($numbers_array, $sum3));
The above should output:
bool(true)
bool(true)
bool(false)
Because 7 + 8 = 15, 7 + 8 + 9 = 24, but no combination of 1-9 can create 200.
Here's my EXTREMELY slow solution:
function is_summable($numbers, $sum) {
//Sort provided numbers and assign numerical keys.
asort($numbers);
$numbers = array_values($numbers);
//Var for additions and var for number of provided numbers.
$total = 0;
$numbers_length = count($numbers);
//Empty var to fill below.
$code = '';
//Loop and add for() loops.
for ($i = 0; $i < $numbers_length; $i++) {
$code .= 'for ($n' . $i . ' = 0; $n' . $i . ' < ' . $numbers_length . '; $n' . $i . '++) {';
if ($i != 0) {
$code .= 'if ($n' . $i . ' != $n' . ($i - 1) . ') {';
}
$code .= '$total += intval($numbers[$n' . $i . ']);';
$code .= 'if ($total == $sum) {';
$code .= 'return true;';
$code .= '}';
}
//Add ending bracket for for() loops above.
for ($l = 0; $l < $numbers_length; $l++) {
$code .= '$total -= intval($numbers[$n' . $i . ']);';
if ($l != 0) {
$code .= '}';
}
$code .= '}';
}
//Finally, eval the code.
eval($code);
//If "true" not returned above, return false.
return false;
}
$num_arr = array(1,2,3,4,5,6,7,8,9);
var_dump(is_summable($num_arr, 24));
http://pastebin.com/1nawuwXK
As always, help is appreciated!!
Your problem is in fact a standard algorithmic problem (as Jon mentioned knapsack problem), more specifically Subset sum problem. It can be solved in polynomial time (have look on wiki page).
Pseudocode:
initialize a list S to contain one element 0.
for each i from 1 to N do
let T be a list consisting of xi + y, for all y in S
let U be the union of T and S
sort U
make S empty
let y be the smallest element of U
add y to S
for each element z of U in increasing order do
//trim the list by eliminating numbers close to one another
//and throw out elements greater than s
if y + cs/N < z ≤ s, set y = z and add z to S
if S contains a number between (1 − c)s and s, output yes, otherwise no
Related
I want to merge cells dynamically based on count using PHPEXCEl.
For example:
if $count = 2;
I want to merge two cells as given below,
$objPHPExcel->getActiveSheet()->mergeCells('A1:B1');
similarly, if $count = 4;
$objPHPExcel->getActiveSheet()->mergeCells('C1:F1');
similarly, if $count = 5;
$objPHPExcel->getActiveSheet()->mergeCells('G1:K1');
I want to get this logic in a loop.
I tried the below logic, which doesn't work
$count = ew_Execute("SELECT COUNT(*) FROM ems_defects_codes WHERE DEF_CODE = '$def_code'");
$start_letter = A;
$rowno = 1;
for ($i = 0; $i < $count ; $i++) {
$objPHPExcel->getActiveSheet()->mergeCells($start_letter.$rowno.':'.$i.$rowno);
}
Any help will be much appreciated.Thanks..!!
You need to get column range string value for the inputs - start_letter, row_number and count. Once the column range is available, same can be used in the PHPExcel mergeCells function. Here is example code to get column range:
function getColRange($start_letter, $row_number, $count) {
$alphabets = range('A', 'Z');
$start_idx = array_search(
$start_letter,
$alphabets
);
return sprintf(
"%s%s:%s%s",
$start_letter,
$row_number,
$alphabets[$start_idx + $count],
$row_number
);
}
print getColRange('A', 1, 2) . PHP_EOL;
print getColRange('C', 1, 4) . PHP_EOL;
print getColRange('G', 1, 4) . PHP_EOL;
Output
A1:C1
C1:G1
G1:K1
Further you can use this new function with your code to do actual merge. You can choose to call this function or in a loop.
$sheet = $objPHPExcel->getActiveSheet();
$sheet->mergeCells(
getColRange(
$start_letter,
$row_number,
$count
)
);
The problem is that your $i inside of your loop is always going to be an integer; you need to convert that integer to the corresponding index of the alphabet, by creating an alphabetic array. This can be done with a simple range('A', 'Z').
You also need to wrap the A in $start_letter in apostrophes (as 'A'), and now that the range has been created, you can simply use the index of the alphabet for that:$start_letter = 0 (later becoming 'A' with $alphabet[$start_letter]).
Then you'll need to add the starting letter to the count for in order to get the ending cell in mergeCells(). Your starting cell now becomes $alphabet[$start_letter] . $rowno, and your ending cell now becomes ($alphabet[$start_letter] + $alphabet[$i]) . $rowno.
This can be seen in the following:
$count = ew_Execute("SELECT COUNT(*) FROM ems_defects_codes WHERE DEF_CODE = '$def_code'");
$alphabet = range('A', 'Z');
$start_letter = 0;
$rowno = 1;
for ($i = 0; $i < $count; $i++) {
$objPHPExcel->getActiveSheet()->mergeCells($alphabet[$start_letter] . $rowno . ':' . ($alphabet[$start_letter] + $alphabet[$i]) . $rowno);
}
I've written a small PHP function to find a length of a longest palindromic substring of a string. To avoid many loops I've used a recursion.
The idea behind algorithm is, to loop through an array and for each center (including centers between characters and on a character), recursively check left and right caret values for equality. Iteration for a particular center ends when characters are not equal or one of the carets is out of the array (word) range.
Questions:
1) Could you please write a math calculations which should be used to explain time complexity of this algorithm? In my understanding its O(n^2), but I'm struggling to confirm that with a detailed calculations.
2) What do you think about this solution, any improvement suggestions (considering it was written in 45 mins just for practice)? Are there better approaches from the time complexity perspective?
To simplify the example I've dropped some input checks (more in comments).
Thanks guys, cheers.
<?php
/**
* Find length of the longest palindromic substring of a string.
*
* O(n^2)
* questions by developer
* 1) Is the solution meant to be case sensitive? (no)
* 2) Do phrase palindromes need to be taken into account? (no)
* 3) What about punctuation? (no)
*/
$input = 'tttabcbarabb';
$input2 = 'taat';
$input3 = 'aaaaaa';
$input4 = 'ccc';
$input5 = 'bbbb';
$input6 = 'axvfdaaaaagdgre';
$input7 = 'adsasdabcgeeegcbgtrhtyjtj';
function getLenRecursive($l, $r, $word)
{
if ($word === null || strlen($word) === 0) {
return 0;
}
if ($l < 0 || !isset($word[$r]) || $word[$l] != $word[$r]) {
$longest = ($r - 1) - ($l + 1) + 1;
return !$longest ? 1 : $longest;
}
--$l;
++$r;
return getLenRecursive($l, $r, $word);
}
function getLongestPalSubstrLength($inp)
{
if ($inp === null || strlen($inp) === 0) {
return 0;
}
$longestLength = 1;
for ($i = 0; $i <= strlen($inp); $i++) {
$l = $i - 1;
$r = $i + 1;
$length = getLenRecursive($l, $r, $inp); # around char
if ($i > 0) {
$length2 = getLenRecursive($l, $i, $inp); # around center
$longerOne = $length > $length2 ? $length : $length2;
} else {
$longerOne = $length;
}
$longestLength = $longerOne > $longestLength ? $longerOne : $longestLength;
}
return $longestLength;
}
echo 'expected: 5, got: ';
var_dump(getLongestPalSubstrLength($input));
echo 'expected: 4, got: ';
var_dump(getLongestPalSubstrLength($input2));
echo 'expected: 6, got: ';
var_dump(getLongestPalSubstrLength($input3));
echo 'expected: 3, got: ';
var_dump(getLongestPalSubstrLength($input4));
echo 'expected: 4, got: ';
var_dump(getLongestPalSubstrLength($input5));
echo 'expected: 5, got: ';
var_dump(getLongestPalSubstrLength($input6));
echo 'expected: 9, got: ';
var_dump(getLongestPalSubstrLength($input7));
Your code doesn't really need to be recursive. A simple while loop would do just fine.
Yes, complexity is O(N^2). You have N options for selecting the middle point. The number of recursion steps goes from 1 to N/2. The sum of all that is 2 * (N/2) * (n/2 + 1) /2 and that is O(N^2).
For code review, I wouldn't do recursion here since it's fairly straightforward and you don't need the stack at all. I would replace it with a while loop (still in a separate function, to make the code more readable).
I was trying to print multiplication of big numbers and they are resulting in float type scientific number.
var_dump((double)('290287121823'*'290287121823'));
I tried the function number_format and preg_replace to remove all ','. But after number_format the result is not correct.
Used following code:
preg_replace("/,/", "", (number_format(('290287121823'*'290287121823'))));
Output received: 84266613096281242861568
Expected correct output: 84266613096281243382112
The large numbers are computed digits by digits. Like we learn at school (see Long multiplication).
ex:
// 17
// x 27
// ----
// 119
// + 34
// -----
// = 459
Here is a function (which should be optimized) but shows you the principle.
echo bn_mul('17', '27'), PHP_EOL; // 459
echo bn_mul('157', '27'), PHP_EOL; // 4239
echo bn_mul('1234', '6627'), PHP_EOL; // 8177718
echo bn_mul('23958233', '5830'), PHP_EOL; // 139676498390
echo bn_mul('290287121823', '290287121823'), PHP_EOL; // 84266613096281242843329
Implementation:
function bn_mul($n2, $n1) {
$l1 = strlen($n1);
$l2 = strlen($n2);
$rows = [];
for ($idx1 = $l1 - 1 ; $idx1 >= 0 ; $idx1--) {
// get digit
$d1 = substr($n1, $idx1, 1) ;
$carry = 0; // reset carry
$row = []; // store digit of $d1 x each digits of $n2
// prepend 0 (10 * num rows)
for ($x=0 ; $x<count($rows) ; $x++) $row[] = 0;
for ($idx2 = $l2 - 1 ; $idx2 >= 0 ; $idx2--) {
// get digit
$d2 = substr($n2, $idx2, 1) ;
// multiplication of digit 1 x digit 2 + current carry
$r = $d1 * $d2 + $carry;
$carry = 0;
// compute carry
if ($r >= 10) {
$carry = substr($r, 0, -1);
$r = substr($r, -1);
}
$row[] = $r ;
}
if ($carry) $row[] = $carry;
$rows[] = $row ;
}
// Sum digits of rows
$total = [] ;
$carry = 0 ;
for ($x=0;$x < count(end($rows)) ; $x++){
$tot = $carry;
$carry = 0;
foreach ($rows as $row){
if (isset($row[$x])) $tot += $row[$x] ;
}
while ($tot >= 10) {
$tot -= 10;
$carry++;
}
$total[$x] = $tot;
}
return strrev(implode($total));
}
You should keep in mind that even if PHP is NOT typesafe, there are types in the background.
When you look up the documentation you'll find out, that floating points only have a precision up to 14 digits. Therefore, all trailing numbers are "cut off". The magnitude remains and it will print in scientific format but you can't really know what's "below" that 14 digits. Therefore, your try to convert the result is doomed to fail in the first place.
Example:
12345678910111213
^^^ get's cut off
(Working on a search algorithm) I want to iterate over possible matches with two bits set in a 16bit word. Seems like a silly problem with a currently overly-complex solution.
Iteration should return (decimal) 3,5,6,9,10,12,17...
What's the proper word for the problem? Bit-mask-looping?
Any clever function for this?
Current code - now updated:
(As it stands, i guess there's no easier way around this.)
<?php
function biterate($numBits=8, $setBits=2, $maxval=null) {
//init
if(is_null($maxval)) $maxval = (pow(2,$setBits)-1) * pow(2,$numBits - $setBits);
$err = 0;
header('content-type:text/plain');
echo '-- ' . $setBits . ' of ' . $numBits . " --\r\n";
$result = str_pad('', $numBits - $setBits, '0') . str_pad('', $setBits, '1');
do {
$err++;
if($err > 200) die('bad code');
//echo and calc next val.
echo $result . ' : ' . bindec($result) . "\r\n";
//count set bits and search for '01' to be replaced with '10'. From LSB.
$bitDivend = '';
$hit = false;
for($i=$numBits;$i>0;$i--) {
if(substr($result,$i-2,2) == '01') {
$hit = true;
//do the replacement and replace the lower part with bitDivend.
$result = substr($result, 0, $i-2) . '10';
$result .= str_pad('',$numBits - $i - strlen($bitDivend),'0');
$result .= $bitDivend;
//exit loop
$i = 0;
}
if($result[$i-1] == '1') $bitDivend .= '1';
}
} while($hit && bindec($result) <= $maxval);
}
biterate(8,2);
biterate(8,7);
biterate();
If you just want all the 16 bit ints with 2 bits set, the following code should do it:
<?php
for($i=1;$i<16;$i++)
{
for($j=0;$j<$i;$j++)
{
echo (1<<$i)|(1<<$j) , "\r\n";
}
}
?>
If you look at the bit patterns of the numbers you can see how it works:
11 3
101 5
110 6
1001 9
1010 10
1100 12
10001 17
10010 18
10100 20
11000 24
etc. You just move the most significant bit one place to the left (another power of 2) for each iteration of the outer loop, and inside the inner loop you iterate from the least significant bit (1) to 1 place to the right of the most significant bit.
If you wanted to generalise this to support an arbitrary number of bits and places, you could extend the above algorithm using recursion:
<?php
function biterate_recursive($numBits=8, $setBits=2, $initialValue=0, $maxval=null) {
for($i=$setBits-1;$i<$numBits;$i++)
{
if(!is_null($maxval) && ($initialValue|(1<<$i)) > $maxval)
break;
if($setBits==1)
echo $initialValue|(1<<$i) , "\r\n";
else
biterate_recursive($i, $setBits-1, $initialValue|(1<<$i), $maxval);
}
}
biterate_recursive(16, 2);
?>
You can also think of the problem as just choosing combinations i.e. C(16,2) choosing 2 numbers a,b from the set 0-15, and then calculating (1<<a)|(1<<b). However you have to be careful about your choice of combination algorithm if you want to get the numbers in order.
I have a clumsy PHP code that I've used to get approximate fraction numbers for irrational numbers like pi, phi, square root of 2, 3 and so on. I'd like to get a formula that I can use on MatLab and get both data table and draw a plot based on approximate fraction numbers. Maybe someone already can grab from this but I'll provide PHP code to complement the case:
$n = phi(); # irrational number (imaginary/complex number?)
$x = 500; # how many numbers to check
$max = 50; # how many instances to show
$precision = 0.0001;
# check every i against every j and make a comparison how near their values are to each other
for ($i=1; $i<$x; $i++) {
for ($j=1; $j<$x; $j++) {
# compared value is stored on array. very distant numbers needs to be discarded ($precision) or array gets easily too big, limit 64k
if (($d = abs(($n - ($i/$j)))) && $d > $precision) continue;
$c[] = array($i, $j, $d);
}
}
# sort comparison chart by third index (2)
array_qsort($c, 2);
# print max best values from the sorted comparison chart
$count = count($c);
echo "closest fraction numbers for $n from $count calculated values are:<br />\n<br />\n";
$r = 0;
foreach ($c as $abc) {
$r++;
$d = $abc[0]/$abc[1];
echo $abc[0] . '/' . $abc[1] . ' = ' . $d . ' (' . round($abc[2]*(1/$precision), 10) . ')' . "<br />\n";
if ($r > $max) break;
}
There are more efficient algorithms, here is one:
function [a, b, c] = approxfrac( r, precision )
a = floor(r);
r = r - a;
if r==0,
b=0;
c=1;
return
end
p1 = 0; q1 = 1;
p2 = 1; q2 = 1;
b = p1+p2;
c = q1+q2;
while abs(r-b/c) > precision,
if r>b/c,
p1 = b; q1 = c;
else
p2 = b; q2 = c;
end
b = p1+p2;
c = q1+q2;
end
end
There's a function for that: rat