Detecting if integer can be written as sum of given integers - php

Supposing I'm having the constants 3,5,6,9,10. How can I detect how to write $n, which is the input, as a sum of these constants with the least number of terms?
Examples
$n=10, S=10
$n=18, S=9+9
$n=24, S=9+9+6
$n=27, S=9+9+9
$n=28, S=10+9+9
Thanks

This is another Python solution, but hopefully it's easy for you to convert to PHP (I would do it myself, but I'm no PHP expert - I'm sure you could do a better job of it). I've tried not to use any advanced Python funcitons, so that it is easier for non-Python readers to understand, but if some Python syntax is not clear, please just ask.
allowed = [3, 5, 6, 9, 10]
n = 28
solutions = [ None ] * (n + 1)
solutions[0] = []
for i in range(n + 1):
if solutions[i] is None: continue
for a in allowed:
if i + a > n: continue
if solutions[i + a] is None or len(solutions[i]) + 1 < len(solutions[i + a]):
solutions[i + a] = solutions[i] + [a]
print solutions[28]
It works by starting from 0 and building up to the desired number, keeping a cache of the shortest solution seen so far for each possible total. It has a running time of O(n * a), where a is the number of different allowed values.
By the way, your answer to n=28 is wrong. It should be [9, 9, 10].
Update: here's my attempt at a PHP solution:
<?php
$allowed = array(3, 5, 6, 9, 10);
$n = 28;
$solutions = array();
$solutions[0] = array();
foreach (range(0, $n) as $i) {
if (is_null($solutions[$i])) continue;
foreach ($allowed as $a) {
if ($i + $a > $n) continue;
if (is_null($solutions[$i + $a]) ||
sizeof($solutions[$i]) + 1 < sizeof($solutions[$i + $a])) {
$solutions[$i + $a] = array_merge($solutions[$i], array($a));
}
}
}
var_dump($solutions[$n]);
?>
It gives the right answer, but please be aware that I'm not a professional PHP coder - I just looked up the equivalent functions in the PHP documentation.

This is Mark Byers' algorithm, rewritten using loop structures that are more familiar to PHP developers, and constructs that won't generate PHP notices. $C is your set of integers, $S the solutions.
$n = 28;
$C = array(3, 5, 6, 9, 10);
$S = array(array());
// if your set isn't sorted already, you have to call sort()
//sort($C);
for ($i = 0; $i <= $n; ++$i)
{
if (!isset($S[$i]))
{
continue;
}
foreach ($C as $v)
{
if ($i + $v > $n)
{
break;
}
if (!isset($S[$i + $v])
|| count($S[$i + $v]) > 1 + count($S[$i]))
{
$S[$i + $v] = $S[$i];
$S[$i + $v][] = $v;
}
}
}
print_r($S[$n]);

Two obvious approaches suggest themselves:
Write a series of linear equations,
and solve to find various solutions.
Choose one with the least number of
terms.
Trial and error, starting
with the largest terms first.

Find all possible solutions for "S=3A+5B+6C+9D+10E" then choose the one with the most 0 values for A,B,C,D,E

a rough sketch of an unscalable but correct solution (sorry, so far its only python ..):
#!/usr/bin/env python
import itertools, sys
pool = [3, 5, 6, 9, 10]
repeat, found, solutions = 1, False, set()
try: x = int(sys.argv[1])
except: x = 42
while not found:
for n in itertools.product(pool, repeat=repeat):
s = sum(n)
if s == x:
solutions.add(n)
found = True
break
repeat = repeat + 1
print solutions
would yield:
$ python 1850629.py 11
set([(5, 6)])
$ python 1850629.py 19
set([(9, 10)])
$ python 1850629.py 21
set([(3, 9, 9)])
$ python 1850629.py 42
set([(3, 9, 10, 10, 10)])

In addition to the excellent general answers already provided, bear in mind that if your set of values has certain properties, much more optimal solutions exist.
Specifically, if your solution is 'minimal' - that is, a single best solution exists for any value - then you can find the smallest number of elements using a 'greedy' algorithm: Simply add the largest value until the remainder is smaller than it, repeat with the next largest value, and so forth.
As an example, the denominations used for money in many countries are .01, .02, .05, .10, .20, .50, 1, 2, 5, .... This set is minimal, so you can just repeatedly add the largest valid denomination.

NP-complete problem
Subset sum problem

Related

Find the smallest positive integer that does not occur in an array

I am trying out the following codility.com exercise to improve my skills online, I was presented with the following problem.
This is a demo task.
Write a function:
class Solution { public int solution(int[] A); }
that, given an array A of N integers, returns the smallest positive
integer (greater than 0) that does not occur in A.
For example,
given A = [1, 3, 6, 4, 1, 2], the function should return 5.
Given A = [1, 2, 3], the function should return 4.
Given A = [-1, -3], the function should return 1.
Write an efficient algorithm for the following assumptions:
• N is an
integer within the range [1..100,000);
• each element of array A is an
integer within the range (-1,000,000..1,000,000).
Copyright 2009– by Codility Limited
rendered description
I solved it using the following solution:
<?php
class Solution {
public function($A) {
$posInts = [1, 2, 3, 4, 5, 6, 7, 8, 9];
$diffs = array_diff($postInts, $A);
$smallestPosInt = min($diffs);
return $smallestPosInt;
}
}
However upon submitting I got the following score:
Now I am very unsure of what I did wrong here or how I can rewrite the code with a better algorithm.
Check out this answer using Javascript in a way that works with the best possible performance -If I am not mistaken- O(N).
function solution(A) {
const set = new Set(A)
let i = 1
while (set.has(i)) {
i++
}
return i
}
I would just loop over (increment) any possible integers:
function solution($A) {
$result = 1;
$maxNumber = max($A);
for (; $result <= $maxNumber; $result++) {
if (!in_array($result, $A)) {
break;
}
}
return $result;
}
var_dump(solution([1, 3, 6, 4, 1, 2])); // int(5)
var_dump(solution([1, 2, 3])); // int(4)
var_dump(solution([-1, -3])); // int(1)
// As a bonus, this also works for larger numbers:
var_dump(solution([1, 3, 6, 4, 1, 2, 7, 8, 9, 10, 11, 12, 13, 5, 15])); // int(14)
Edit regarding performance:
As pointed out in the comments (and you already said yourself), this is not a very efficient solution.
While I do not have enough time on my hands currently to do real performance testing, I think this should be close to an O(n) solution: (keeping in mind that I am not sure how arrays are implemented on the C-side of PHP)
function solution($A) {
$result = 1;
$maxNumber = max($A);
$values = array_flip($A);
for (; $result <= $maxNumber; $result++) {
if (!isset($values[$result])) {
break;
}
}
return $result;
}
// Not posting the output again because it is naturally the same ;)
The "trick" here is to flip the array first so that the values become the indexes. Since a) we do not care about the original indexes and b) we do not care if duplicated values overwrite each other, we can safely do that.
Using isset() instead of in_array() should be a lot quicker since it basically just checks if a variable (in this case stored at a specific index of the array) exists and PHP does therefore not have to iterate through the array in order to check whether or not each number we loop over exists within it.
P.S.: After thinking twice I think this may still be closer to O(n*2) because max() probably loops to find the highest value. You could also remove that line and just check against the highest number there is in PHP as an emergency exit, like so: for (; $result <= PHP_INT_MAX; $result++) { ... } as a further optimization. Or maybe just hard-code the highest allowed number as specified in the task.
If we're allowed to modify the input, perform this in place, otherwise create a new array of size n + 1:
For each element encountered in the original array, if it is greater than n + 1 or smaller than 1, assign 0 at the element's index (index - 1 if performing in place); otherwise assign 1 at the index of the array the value is and assign 0 at its own index if it is different. After that run a second traversal and report the first index (index + 1 if performing in place) greater than zero with value 0, or n + 1.
[1, 3, 6, 4, 1, 2]
=>
[1, 1, 1, 1, 0, 1]
report 5

Given a number, foreach 3rd digit do something

Lets say i have a number like below:
$number = "20160513123"
So what i want to do is,
Starting from 2, for each third digit i want to multiply and find the sum.
So basically i want to get the number 2,6,1,2 from the string
and multiply each number by 3 and then calculate the sum of all the multiplication.
Like so:
2 * 3,
6 * 3,
1 * 3,
2 * 3,
Then i want to get the sum of all of the above. How is that possible using PHP?
You could use the range function to step through your characters:
$number = "20160513123";
$sum = 0;
foreach (range(0, strlen($number), 3) as $i) {
$sum += 3*$number[$i];
}
echo $sum;
Output:
33
If you like functional programming, then this may be according to your liking:
$sum = 3 * array_sum(array_map(
function($i) use ($number) { return $number[$i]; },
range(0, strlen($number), 3)
));
Or, with the use of str_split, you can get chunks of 3 characters:
$sum = array_sum(array_map(
function($s) { return 3*$s[0]; },
str_split($number, 3)
));
NB: Note that you can either multiply each number with 3 separately, or just the sum.
Compact solution
One final alternative. It uses array_chunk and array_column to filter out the desired digits (it seems PHP has functions for most anything):
$sum = 3 * array_sum(array_column(array_chunk(str_split($number), 3), 0));
Take your pick ;-)
The solution using str_split function(to get an array of digits):
$number = "20160513123";
$total_sum = 0;
foreach (str_split($number) as $k => $n) {
if ($k % 3 == 0) $total_sum += $n * 3; // considering each third digit
}
print_r($total_sum); // 33
CasimiretHippolyte's tiny, tiny one-liner
(you're not going to find a smaller one-liner for this question):
echo array_sum(preg_split('/.\K..?/',$number))*3;
This explodes $number after this first character by every 2 (or 1) character to follow, adds up the remaining numbers and multiplies them. Just clever awesome!
CasimiretHippolyte's previous solution:
if(preg_match_all('/..\K./',"00$number",$matches)){
echo array_sum($matches[0])*3;
}
Though it required left-padding the string, this method used shorter/simpler regex and produced a leaner $matches array, compared to my solution below:
mickmackusa's solution:
if(preg_match_all("/(\d)(?:\d\d|\d$|$)/",$number,$matches)){
echo array_sum($matches[1])*3;
}
My regex matches all single digits that are followed by either: 2 digits,
1 digit & end of line, or end of line. This method steps past the non-capture group digits when it can, so only 1st, 4th, 7th, etc. are captured.
All codes above output:
33

How to calculate geometric average(pascal or php)

I wanna solve this problem with your support.
Assume that, there is an array in variable named $ar, and exist 5 numbers in this array, so i want to calculate geometric average of these numbers through Pascal or PHP programming language. How can i do ?
Here is PHP version:
function geometric_average($a) {
foreach($a as $i=>$n) $mul = $i == 0 ? $n : $mul*$n;
return pow($mul,1/count($a));
}
//usage
echo geometric_average(array(2,8)); //Output-> 4
Possible solution in "standard" Pascal:
program GeometricAvarage;
const SIZE = 5;
function GeoAvg(A:array of real):real;
var
avg: real;
i: integer;
begin
avg := 1;
for i:=0 to (SIZE) do
avg := avg * A[i];
avg :=Exp(1/SIZE*Ln(avg));
Result:=avg;
end;
begin
var
ar: array [1..SIZE] of real :=(1,2,3,4,5);
writeln('Geometric Avarage = ', GeoAvg(ar)); {Output should be =~2.605}
readln;
end.
If you want to use dynamic arrays this should be done in Delphi or ObjectPascal for example.
For someone that had an issue with this, as I have stated in the comment to the PHP answer, that answer may not be suitable for everyone, especially with ones looking to find geometric average/mean for large numbers or large number of numbers as PHP will simply not store it.
Pretty easy solution is to split the initial array into chunks, calculate mean and then multiply them:
function geometricMean(array $array)
{
if (!count($array)) {
return 0;
}
$total = count($array);
$power = 1 / $total;
$chunkProducts = array();
$chunks = array_chunk($array, 10);
foreach ($chunks as $chunk) {
$chunkProducts[] = pow(array_product($chunk), $power);
}
$result = array_product($chunkProducts);
return $result;
}
Note the 10 - it's the number of elements in a chunk, you may change that if you need to do so. If you get INF as a result, try lowering that.

PHP: find two or more numbers from a list of numbers that add up towards a given amount

I am trying to create a little php script that can make my life a bit easier.
Basically, I am going to have 21 text fields on a page where I am going to input 20 different numbers. In the last field I will enter a number let's call it the TOTAL AMOUNT. All I want the script to do is to point out which numbers from the 20 fields added up will come up to TOTAL AMOUNT.
Example:
field1 = 25.23
field2 = 34.45
field3 = 56.67
field4 = 63.54
field5 = 87.54
....
field20 = 4.2
Total Amount = 81.90
Output: field1 + fields3 = 81.90
Some of the fields might have 0 as value because sometimes I only need to enter 5-15 fields and the maximum will be 20.
If someone can help me out with the php code for this, will be greatly appreciated.
If you look at oezis algorithm one drawback is immediately clear: It spends very much time summing up numbers which are already known not to work. (For example if 1 + 2 is already too big, it doesn't make any sense to try 1 + 2 + 3, 1 + 2 + 3 + 4, 1 + 2 + 3 + 4 + 5, ..., too.)
Thus I have written an improved version. It does not use bit magic, it makes everything manual. A drawback is, that it requires the input values to be sorted (use rsort). But that shouldn't be a big problem ;)
function array_sum_parts($vals, $sum){
$solutions = array();
$pos = array(0 => count($vals) - 1);
$lastPosIndex = 0;
$currentPos = $pos[0];
$currentSum = 0;
while (true) {
$currentSum += $vals[$currentPos];
if ($currentSum < $sum && $currentPos != 0) {
$pos[++$lastPosIndex] = --$currentPos;
} else {
if ($currentSum == $sum) {
$solutions[] = array_slice($pos, 0, $lastPosIndex + 1);
}
if ($lastPosIndex == 0) {
break;
}
$currentSum -= $vals[$currentPos] + $vals[1 + $currentPos = --$pos[--$lastPosIndex]];
}
}
return $solutions;
}
A modified version of oezis testing program (see end) outputs:
possibilities: 540
took: 3.0897309780121
So it took only 3.1 seconds to execute, whereas oezis code executed 65 seconds on my machine (yes, my machine is very slow). That's more than 20 times faster!
Furthermore you may notice, that my code found 540 instead of 338 possibilities. This is because I adjusted the testing program to use integers instead of floats. Direct floating point comparison is rarely the right thing to do, this is a great example why: You sometimes get 59.959999999999 instead of 59.96 and thus the match will not be counted. So, if I run oezis code with integers it finds 540 possibilities, too ;)
Testing program:
// Inputs
$n = array();
$n[0] = 6.56;
$n[1] = 8.99;
$n[2] = 1.45;
$n[3] = 4.83;
$n[4] = 8.16;
$n[5] = 2.53;
$n[6] = 0.28;
$n[7] = 9.37;
$n[8] = 0.34;
$n[9] = 5.82;
$n[10] = 8.24;
$n[11] = 4.35;
$n[12] = 9.67;
$n[13] = 1.69;
$n[14] = 5.64;
$n[15] = 0.27;
$n[16] = 2.73;
$n[17] = 1.63;
$n[18] = 4.07;
$n[19] = 9.04;
$n[20] = 6.32;
// Convert to Integers
foreach ($n as &$num) {
$num *= 100;
}
$sum = 57.96 * 100;
// Sort from High to Low
rsort($n);
// Measure time
$start = microtime(true);
echo 'possibilities: ', count($result = array_sum_parts($n, $sum)), '<br />';
echo 'took: ', microtime(true) - $start;
// Check that the result is correct
foreach ($result as $element) {
$s = 0;
foreach ($element as $i) {
$s += $n[$i];
}
if ($s != $sum) echo '<br />FAIL!';
}
var_dump($result);
sorry for adding a new answer, but this is a complete new solution to solve all problems of life, universe and everything...:
function array_sum_parts($n,$t,$all=false){
$count_n = count($n); // how much fields are in that array?
$count = pow(2,$count_n); // we need to do 2^fields calculations to test all possibilities
# now i want to look at every number from 1 to $count, where the number is representing
# the array and add up all array-elements which are at positions where my actual number
# has a 1-bit
# EXAMPLE:
# $i = 1 in binary mode 1 = 01 i'll use ony the first array-element
# $i = 10 in binary mode 10 = 1010 ill use the secont and the fourth array-element
# and so on... the number of 1-bits is the amount of numbers used in that try
for($i=1;$i<=$count;$i++){ // start calculating all possibilities
$total=0; // sum of this try
$anzahl=0; // counter for 1-bits in this try
$k = $i; // store $i to another variable which can be changed during the loop
for($j=0;$j<$count_n;$j++){ // loop trough array-elemnts
$total+=($k%2)*$n[$j]; // add up if the corresponding bit of $i is 1
$anzahl+=($k%2); // add up the number of 1-bits
$k=$k>>1; //bit-shift to the left for looking at the next bit in the next loop
}
if($total==$t){
$loesung[$i] = $anzahl; // if sum of this try is the sum we are looking for, save this to an array (whith the number of 1-bits for sorting)
if(!$all){
break; // if we're not looking for all solutions, make a break because the first one was found
}
}
}
asort($loesung); // sort all solutions by the amount of numbers used
// formating the solutions to getting back the original array-keys (which shoud be the return-value)
foreach($loesung as $val=>$anzahl){
$bit = strrev(decbin($val));
$total=0;
$ret_this = array();
for($j=0;$j<=strlen($bit);$j++){
if($bit[$j]=='1'){
$ret_this[] = $j;
}
}
$ret[]=$ret_this;
}
return $ret;
}
// Inputs
$n[0]=6.56;
$n[1]=8.99;
$n[2]=1.45;
$n[3]=4.83;
$n[4]=8.16;
$n[5]=2.53;
$n[6]=0.28;
$n[7]=9.37;
$n[8]=0.34;
$n[9]=5.82;
$n[10]=8.24;
$n[11]=4.35;
$n[12]=9.67;
$n[13]=1.69;
$n[14]=5.64;
$n[15]=0.27;
$n[16]=2.73;
$n[17]=1.63;
$n[18]=4.07;
$n[19]=9.04;
$n[20]=6.32;
// Output
$t=57.96;
var_dump(array_sum_parts($n,$t)); //returns one possible solution (fuc*** fast)
var_dump(array_sum_parts($n,$t,true)); // returns all possible solution (relatively fast when you think of all the needet calculations)
if you don't use the third parameter, it returns the best (whith the least amount numbers used) solution as array (whith keys of the input-array) - if you set the third parameter to true, ALL solutions are returned (for testing, i used the same numbers as zaf in his post - there are 338 solutions in this case, found in ~10sec on my machine).
EDIT:
if you get all, you get the results ordered by which is "best" - whithout this, you only get the first found solution (which isn't necessarily the best).
EDIT2:
to forfil the desire of some explanation, i commented the essential parts of the code . if anyone needs more explanation, please ask
1. Check and eliminate fields values more than 21st field
2. Check highest of the remaining, Add smallest,
3. if its greater than 21st eliminate highest (iterate this process)
4. If lower: Highest + second Lowest, if equal show result.
5. if higher go to step 7
6. if lower go to step 4
7. if its lower than add second lowest, go to step 3.
8. if its equal show result
This is efficient and will take less execution time.
Following method will give you an answer... almost all of the time. Increase the iterations variable to your taste.
<?php
// Inputs
$n[1]=8.99;
$n[2]=1.45;
$n[3]=4.83;
$n[4]=8.16;
$n[5]=2.53;
$n[6]=0.28;
$n[7]=9.37;
$n[8]=0.34;
$n[9]=5.82;
$n[10]=8.24;
$n[11]=4.35;
$n[12]=9.67;
$n[13]=1.69;
$n[14]=5.64;
$n[15]=0.27;
$n[16]=2.73;
$n[17]=1.63;
$n[18]=4.07;
$n[19]=9.04;
$n[20]=6.32;
// Output
$t=57.96;
// Let's try to do this a million times randomly
// Relax, thats less than a blink
$iterations=1000000;
while($iterations-->0){
$z=array_rand($n, mt_rand(2,20));
$total=0;
foreach($z as $x) $total+=$n[$x];
if($total==$t)break;
}
// If we did less than a million times we have an answer
if($iterations>0){
$total=0;
foreach($z as $x){
$total+=$n[$x];
print("[$x] + ". $n[$x] . " = $total<br/>");
}
}
?>
One solution:
[1] + 8.99 = 8.99
[4] + 8.16 = 17.15
[5] + 2.53 = 19.68
[6] + 0.28 = 19.96
[8] + 0.34 = 20.3
[10] + 8.24 = 28.54
[11] + 4.35 = 32.89
[13] + 1.69 = 34.58
[14] + 5.64 = 40.22
[15] + 0.27 = 40.49
[16] + 2.73 = 43.22
[17] + 1.63 = 44.85
[18] + 4.07 = 48.92
[19] + 9.04 = 57.96
A probably inefficient but simple solution with backtracking
function subset_sums($a, $val, $i = 0) {
$r = array();
while($i < count($a)) {
$v = $a[$i];
if($v == $val)
$r[] = $v;
if($v < $val)
foreach(subset_sums($a, $val - $v, $i + 1) as $s)
$r[] = "$v $s";
$i++;
}
return $r;
}
example
$ns = array(1, 2, 6, 7, 11, 5, 8, 9, 3);
print_r(subset_sums($ns, 11));
result
Array
(
[0] => 1 2 5 3
[1] => 1 2 8
[2] => 1 7 3
[3] => 2 6 3
[4] => 2 9
[5] => 6 5
[6] => 11
[7] => 8 3
)
i don't think the answer isn't as easy as nik mentioned. let's ay you have the following numbers:
1 2 3 6 8
looking for an amount of 10
niks solution would do this (if i understand it right):
1*8 = 9 = too low
adding next lowest (2) = 11 = too high
now he would delete the high number and start again taking the new highest
1*6 = 7 = too low
adding next lowest (2) = 9 = too low
adding next lowest (3) = 12 = too high
... and so on, where the perfect answer would simply
be 8+2 = 10... i think the only solution is trying every possible combination of
numbers and stop if the amaunt you are looking for is found (or realy calculate all, if there are different solutions and save which one has used least numbers).
EDIT: realy calculating all possible combiations of 21 numbers will end up in realy, realy, realy much calculations - so there must be any "intelligent" solution for adding numbers in a special order (lik that one in niks post - with some improvements, maybe that will bring us to a reliable solution)
Without knowing if this is a homework assignment or not, I can give you some pseudo code as a hint for a possible solution, note the solution is not very efficient, more of a demonstration.
Hint:
Compare each field value to all field value and at each iteration check if their sum is equal to TOTAL_AMOUNT.
Pseudo code:
for i through field 1-20
for j through field 1-20
if value of i + value of j == total_amount
return i and j
Update:
What you seem to be having is the Subset sum problem, given within the Wiki link is pseudo code for the algorithm which might help point you in the right direction.

A Project Euler Puzzler (specifically in PHP)

There is another recent Project Euler question but I think this is a bit more specific (I'm only really interested in PHP based solutions) so I'm asking anyway.
Question #5 tasks you with: "What is the smallest number that is evenly divisible by all of the numbers from 1 to 20?"
Now, I have solved it twice. Once very inefficiently and once much more efficiently but I am still far away from an especially sophisticated answer (and I am not especially solid in math hence my brute force solution). I can see a couple of areas where I could improve this but I am wondering if any of you could demonstrate a more efficient solution to this problem.
*spoiler: Here is my less than optimal (7 seconds to run) but still tolerable solution (not sure what to do about the double $... just pretend you only see 1...
function euler5(){
$x = 20;
for ($y = 1; $y < 20; $y++) {
if (!($x%$y)) {
} else {
$x+=20;
$y = 1;
}
}echo $x;
};
Collect prime factors for all numbers between 1 and 20. Counting the maximal exponents of each prime factor, we have 16 = 2**4, 9 = 3**2, as well as 5, 7, 11, 13, 17, 19 (each appearing only once). Multiply the lot, and you have your answer.
in php it will look like this:
<?php
function gcd($a,$b) {
while($a>0 && $b>0) {
if($a>$b) $a=$a-$b; else $b=$b-$a;
}
if($a==0) return $b;
return $a;
}
function euler5($i=20) {
$euler=$x=1;
while($x++<$i) {
$euler*=$x/gcd($euler,$x);
}
return $euler;
}
?>
Its at least twice as fast than what you posted.
Chris Jester-Young is right.
In general if you wanted the smallest number that is evenly divisible by all of the numbers from 1 to N, you would want to find all the prime numbers from 2 to N, and for each one, find the greatest number of times it divides any number in the range. This can be calculated by finding the greatest power of the prime that's not greater than N.
In the case of 20, as Chris pointed out, 2^4 is the greatest power of 2 not greater than 20, and 3^2 is the greatest power of 3 not greater than 20, and for all other primes, only the first power is not greater than 20.
You can remove some numbers that are divided with, for example 1 is unnecessary, all natural numbers are divisible by 1.you don’t need 2 either, and therefore, all numbers are divisible by multiples of 2 (4, 8, 16, etc) are divisible by 2, also. So the relevant numbers will be 11, 12, 13, 14, 15, 16, 17, 18, and 19.
So:
<?
function eulerPuzzle()
{
$integers = array( 11,12,13,14,15,16,17,18,19 );
for ($n = 20; 1; $n += 20 ) {
foreach ($integers as $int) {
if ( $n % $int ) {
break;
}
if ( $int == 19 ) {
die ("Result:" . $n);
}
}
}
}
eulerPuzzle();
?>
<?php
$i=20;
while ($i+=20) {
for ($j=19;$j!==10;--$j){
if ($i%$j) continue 2;
}
die ("result: $i\n");
}
Is the fastest and shortest php solution so far. About 1.4x faster than Czimi's on my comp. But check out the python solution, thats a nice algo.
Some people really over-think this...
In Ruby:
puts 5*7*9*11*13*16*17*19
#People doing simple math; I'm not sure if that is the goal of the exercise. You are to learn new languages and new ways to perform stuff. Just doing it by a calculator isn't the right way going about things.
And I know this is a post in an old old thread but it still comes up in google results :)
Doing it in code (PHP that is) I found this to be the fastest solution:
function eulerPuzzle() {
$integers = array (11, 12, 13, 14, 15, 16, 17, 18, 19 );
for($n = 2520; 1; $n += 2520) {
foreach ( $integers as $int ) {
if ($n % $int) {
break;
}
if ($int == 19) {
die ( "Result:" . $n );
}
}
}
}
eulerPuzzle ();
Yes, it's a modified piece from CMS. The main reason it is faster is because when you read the question, they already state that the lowest possible number for the first 10 integers is 2520. therefor, you can just increment by 2520 instead of 20. resulting in 126 times less loops
I know you said PHP, but here's my rough draft in Python.
#!/usr/bin/env python
from operator import mul
def factor(n):
factors = {}
i = 2
while i < n and n != 1:
while n % i == 0:
try:
factors[i] += 1
except KeyError:
factors[i] = 1
n = n / i
i += 1
if n != 1:
factors[n] = 1
return factors
base = {}
for i in range(2, 2000):
for f, n in factor(i).items():
try:
base[f] = max(base[f], n)
except KeyError:
base[f] = n
print reduce(mul, [f**n for f, n in base.items()], 1)
It's not as elegant as I could have made it, but it calculates the least common multiple of the numbers from 2 to 2000 in .15s. If your iterative solution could process a billion candidates per second, it would take 10^849 years to finish.
In other words, don't bother optimizing the wrong algorithm.

Categories