For an endless number such as Pi, how would one go about finding the first occurrence of an exact sum of digits for a given number n.
For example. If n=20
Pi=3.14159265358979323846264338327950288419716939...
then the first occurrence is from digit 1 to digit 5 since:
1+4+1+5+9=20
if n=30, then the first occurrence is from digit 5 to digit 11
since 9+2+6+5+3+5=30
answer should have a working php demo
The answer to this is using sliding window that will maintain the sum. so maintain two pointers say i and j. Keep increasing j and adding the elements inside. when it crosses the desired sum increase i and decrease the element at i. Then keep increasing j until the sum is reached or the sum overflows so you repeat the above process.
Example sum = 30
141592653589793238 >> i=j=0 current_sum = 1
141592653589793238 >> i=0 j=6 current_sum=28
in the next iteration adding 5 will result in current_sum>30 so hence you increment i
141592653589793238 >> i=1 j=6 current_sum=27
141592653589793238 >> i=2 j=6 current_sum=23
141592653589793238 >> i=2 j=7 current_sum=28
Keep going in this manner and it will finally reach the window that is equal to the sum =30 . That should break you out of the loop and help you find the answer.
Method 1 (suggested by Ashwin Bhat)
This implementation uses two pivots. The sum of digits between $pivot_a and $pivot_b is computed. Depending on the value of the sum, we increment $pivot_b (if the sum is less) or $pivot_a (if the sum is greater). If the sum is equal to $n, break. The values of the pivots give the appropriate digit indices.
$pi = "314159265358979323846264338327950288419716939";
$n = 30;
$pivot_a = $pivot_b = 0;
$sum = 0;
for( ; $pivot_b < strlen($pi); ) {
if($sum < $n) {
$sum += $pi[$pivot_b++];
} elseif ($sum > $n) {
$sum -= $pi[$pivot_a++];
} else {
print('Solution found from digit '.$pivot_a.' to '.$pivot_b.'.');
exit;
}
}
print('No match was found.');
Method 2
This implementation uses one pivot only, from which it starts summing up the digits. If the sum happens to be greater than the desired value, it resets the sum to zero, shifts the pivot one position and starts the summing again.
$pi = "314159265358979323846264338327950288419716939";
$n = 30;
// Let's sum up all the elements from $pivot until we get the exact sum or a
// number greater than that. In the latter case, shift the $pivot one place.
$pivot = 0;
$sum = 0;
for($k=0 ; $sum != $n && $k < strlen($pi) ; $k++) {
$sum += $pi[$k];
print($pi[$k]);
if($sum > $n) {
print(' = '.$sum.' fail, k='.($pivot+1).PHP_EOL);
$sum = 0;
$k = $pivot++;
} elseif($sum < $n) {
print("+");
}
}
print(' = '.$n.' found from digit '.$pivot.' to '.$k.'.');
The implementation is not very effective but tries to explain the steps. It prints
3+1+4+1+5+9+2+6 = 31 fail, k=1
1+4+1+5+9+2+6+5 = 33 fail, k=2
4+1+5+9+2+6+5 = 32 fail, k=3
1+5+9+2+6+5+3 = 31 fail, k=4
5+9+2+6+5+3 = 30 found from digit 4 to 10.
Here's another approach. It builds an array of sums along the way and, on every iteration, attempts to add the current digit to the previous sums, and so on, while always only keeping the sums that are still relevant (< target).
The function either returns:
an array of 2 values representing the 0-based index interval within the digits,
or null if it couldn't find the target sum
Code:
function findFirstSumOccurrenceIndexes(string $digits, int $targetSum): ?array
{
$sums = [];
for ($pos = 0, $length = strlen($digits); $pos < $length; $pos++) {
$digit = (int)$digits[$pos];
if ($digit === $targetSum) {
return [$pos, $pos];
}
foreach ($sums as $startPos => $sum) {
if ($sum + $digit === $targetSum) {
return [$startPos, $pos];
}
if ($sum + $digit < $targetSum) {
$sums[$startPos] += $digit;
}
else {
unset($sums[$startPos]);
}
}
$sums[] = $digit;
}
return null;
}
Demo: https://3v4l.org/9t3vf
Related
I seen in the selection sort if inner equals to minimum value it will be swapping them. but why you do it?
I just added the if statement for swapping if $inner = $min so why swapping them because they are the same index!. So why you do it?!!
The condition is : if $inner = $minimum (Don't swap). else they are not euqal (swap).
this is the code.
<?php
$a = array(10,9,8,7,6,5,4,3,2,1);
$num = sizeof($a);
$comp = 0;
$swap = 0;
for ($i=0; $i < $num; $i++) {
echo "$a[$i] | ";
}
echo "<br>";
for ($in=0; $in < $num; $in++) {
$min = $in;
for ($i=$in+1; $i < $num; $i++) {
if ($a[$i] < $a[$min]) {
$min = $i;
}
$comp++;
}
if ($in != $min) {
$past = $a[$min];
$a[$min] = $a[$in];
$a[$in] = $past;
$swap++;
}
}
for ($i=0; $i < $num; $i++) {
echo "$a[$i] | ";
}
echo "<br> comp : $comp , swap : $swap";
?>
No. Time complexity doesn't depend upon number of swaps you did but on number of iterations for checking.
Lets take an example.,
1 4 9 3 0 8 5
You're saying that you'll iterate the whole array and find the lowest(minimum) number and you'll swap it with current index if both are not equal.
That means,
Your sorted array: 0 4 9 3 1 8 5
But it's not sorted. It means it is sorted upto 0th [1st number] index only.
You've to repeat it the same from 2nd index.
Now, lets find out the actual time complexity.
As you iterate for every index (let's take as 'i'), it is O(N) for outer loop and you'll iterate inner loop from i+1 (let's take as 'j') and it's O(N) for inner loop.
So O(N)*O(N)=O(N^2)
If you still have any doubt, lets take iterations of j that is,
for i=0, j iterates from 1 -> N
for i=1, j iterates from 2 -> N
for i=2, j iterates from 3 -> N and so on...
so = (N-1) + (N-2) + (N-3) + (N-4) ......+ 1
=> (N (N-1)) / 2
= O(N^2)
I need to find a (the next) fibonacci number given a integer N. So let's say I have n = 13 and I need to output the next fibonacci number which is 21 but how do I do this? How can I find the previous number that summed up to form it?
I mean I could easily come up with a for/while loop that returns the fibonacci sequence but how can I find the next number by being given the previous one.
<?php
$n = 13;
while($n < 1000) {
$n = $x + $y;
echo($n."<br />");
$x = $y;
$y = $n;
}
?>
You can use Binet's Formula:
n -n
F(n) = phi - (-phi)
---------------
sqrt(5)
where phi is the golden ratio (( 1 + sqrt(5) ) / 2) ~= 1.61803...
This lets you determine exactly the n-th term of the sequence.
Using a loop you could store the values in an array that could stop immediately one key after finding the selected number in the previous keys value.
function getFib($n) {
$fib = array($n+1); // array to num + 1
$fib[0] = 0; $fib[1] = 1; // set initial array keys
$i;
for ($i=2;$i<=$n+1;$i++) {
$fib[$i] = $fib[$i-1]+$fib[$i-2];
if ($fib[$i] > $n) { // check if key > num
return $fib[$i];
}
}
if ($fib[$i-1] < $n) { // check if key < num
return $fib[$i-1] + $n;
}
if ($fib[$i] = $n-1) { // check if key = num
return $fib[$i-1] + $fib[$i-2];
}
if ($fib[$i-1] = 1) { // check if num = 1
return $n + $n;
}
}
$num = 13;
echo "next fibonacci number = " . getFib($num);
Please note that I haven't tested this out and the code could be optimized, so before downvoting consider this serves only as a concept to the question asked.
You can do it in 1 step:
phi = (1+sqrt(5))/2
next = round(current*phi)
(Where round is a function that returns the closest integer; basically equivalent to floor(x+0.5))
For example, if your current number is 13: 13 * phi = 21.034441853748632, which rounds to 21.
I need to generate x amount of random odd numbers, within a given range.
I know this can be achieved with simple looping, but I'm unsure which approach would be the best, and is there a better mathematical way of solving this.
EDIT: Also I cannot have the same number more than once.
Generate x integer values over half the range, and for each value double it and add 1.
ANSWERING REVISED QUESTION: 1) Generate a list of candidates in range, shuffle them, and then take the first x. Or 2) generate values as per my original recommendation, and reject and retry if the generated value is in the list of already generated values.
The first will work better if x is a substantial fraction of the range, the latter if x is small relative to the range.
ADDENDUM: Should have thought of this approach earlier, it's based on conditional probability. I don't know php (I came at this from the "random" tag), so I'll express it as pseudo-code:
generate(x, upper_limit)
loop with index i from upper_limit downto 1 by 2
p_value = x / floor((i + 1) / 2)
if rand <= p_value
include i in selected set
decrement x
return/exit if x <= 0
end if
end loop
end generate
x is the desired number of values to generate, upper_limit is the largest odd number in the range, and rand generates a uniformly distributed random number between zero and one. Basically, it steps through the candidate set of odd numbers and accepts or rejects each one based how many values you still need and how many candidates still remain.
I've tested this and it really works. It requires less intermediate storage than shuffling and fewer iterations than the original acceptance/rejection.
Generate a list of elements in the range, remove the element you want in your random series. Repeat x times.
Or you can generate an array with the odd numbers in the range, then do a shuffle
Generation is easy:
$range_array = array();
for( $i = 0; $i < $max_value; $i++){
$range_array[] .= $i*2 + 1;
}
Shuffle
shuffle( $range_array );
splice out the x first elements.
$result = array_slice( $range_array, 0, $x );
This is a complete solution.
function mt_rands($min_rand, $max_rand, $num_rand){
if(!is_integer($min_rand) or !is_integer($max_rand)){
return false;
}
if($min_rand >= $max_rand){
return false;
}
if(!is_integer($num_rand) or ($num_rand < 1)){
return false;
}
if($num_rand <= ($max_rand - $min_rand)){
return false;
}
$rands = array();
while(count($rands) < $num_rand){
$loops = 0;
do{
++$loops; // loop limiter, use it if you want to
$rand = mt_rand($min_rand, $max_rand);
}while(in_array($rand, $rands, true));
$rands[] = $rand;
}
return $rands;
}
// let's see how it went
var_export($rands = mt_rands(0, 50, 5));
Code is not tested. Just wrote it. Can be improved a bit but it's up to you.
This code generates 5 odd unique numbers in the interval [1, 20]. Change $min, $max and $n = 5 according to your needs.
<?php
function odd_filter($x)
{
if (($x % 2) == 1)
{
return true;
}
return false;
}
// seed with microseconds
function make_seed()
{
list($usec, $sec) = explode(' ', microtime());
return (float) $sec + ((float) $usec * 100000);
}
srand(make_seed());
$min = 1;
$max = 20;
//number of random numbers
$n = 5;
if (($max - $min + 1)/2 < $n)
{
print "iterval [$min, $max] is too short to generate $n odd numbers!\n";
exit(1);
}
$result = array();
for ($i = 0; $i < $n; ++$i)
{
$x = rand($min, $max);
//not exists in the hash and is odd
if(!isset($result{$x}) && odd_filter($x))
{
$result[$x] = 1;
}
else//new iteration needed
{
--$i;
}
}
$result = array_keys($result);
var_dump($result);
I'm struggling with Project Euler problem 23: Non-abundant sums.
I have a script, that calculates abundant numbers:
function getSummOfDivisors( $number )
{
$divisors = array ();
for( $i = 1; $i < $number; $i ++ ) {
if ( $number % $i == 0 ) {
$divisors[] = $i;
}
}
return array_sum( $divisors );
}
$limit = 28123;
//$limit = 1000;
$matches = array();
$k = 0;
while( $k <= ( $limit/2 ) ) {
if ( $k < getSummOfDivisors( $k ) ) {
$matches[] = $k;
}
$k++;
}
echo '<pre>'; print_r( $matches );
I checked those numbers with the available on the internet already, and they are correct. I can multiply those by 2 and get the number that is the sum of two abundant numbers.
But since I need to find all numbers that cannot be written like that, I just reverse the if statement like this:
if ( $k >= getSummOfDivisors( $k ) )
This should now store all, that cannot be created as the sum of to abundant numbers, but something is not quit right here. When I sum them up I get a number that is not even close to the right answer.
I don't want to see an answer, but I need some guidelines / tips on what am I doing wrong ( or what am I missing or miss-understanding ).
EDIT: I also tried in the reverse order, meaning, starting from top, dividing by 2 and checking if those are abundant. Still comes out wrong.
An error in your logic lies in the line:
"I can multiply those by 2 and get the number that is the sum of two abundant numbers"
You first determine all the abundant numbers [n1, n2, n3....] below the analytically proven limit. It is then true to state that all integers [2*n1, 2*n2,....] are the sum of two abundant numbers but n1+n2, and n2+n3 are also the sum of two abundant numbers. Therein lies your error. You have to calculate all possible integers that are the sum of any two numbers from [n1, n2, n3....] and then take the inverse to find the integers that are not.
I checked those numbers with the available on the internet already, and they are correct. I can multiply those by 2 and get the number that is the sum of two abundant numbers.
No, that's not right. There is only one abundant number <= 16, but the numbers <= 32 that can be written as the sum of abundant numbers are 24 (= 12 + 12), 30 (= 12 + 18), 32 (= 12 + 20).
If you have k numbers, there are k*(k+1)/2 ways to choose two (not necessarily different) of them. Often, a lot of these pairs will have the same sum, so in general there are much fewer than k*(k+1)/2 numbers that can be written as the sum of two of the given k numbers, but usually, there are more than 2*k.
Also, there are many numbers <= 28123 that can be written as the sum of abundant numbers only with one of the two abundant numbers larger than 28123/2.
This should now store all, that cannot be created as the sum of to abundant numbers,
No, that would store the non-abundant numbers, those may or may not be the sum of abundant numbers, e.g. 32 is a deficient number (sum of all divisors except 32 is 31), but can be written as the sum of two abundant numbers (see above).
You need to find the abundant numbers, but not only to half the given limit, and you need to check which numbers can be written as the sum of two abundant numbers. You can do that by taking all pairs of two abundant numbers (<= $limit) and mark the sum, or by checking $number - $abundant until you either find a pair of abundant numbers or determine that none sums to $number.
There are a few number theoretic properties that can speed it up greatly.
Below is php code takes 320 seconds
<?php
set_time_limit(0);
ini_set('memory_limit', '2G');
$time_start = microtime(true);
$abundantNumbers = array();
$sumOfTwoAbundantNumbers = array();
$totalNumbers = array();
$limit = 28123;
for ($i = 12; $i <= $limit; $i++) {
if ($i >= 24) {
$totalNumbers[] = $i;
}
if (isAbundant($i)) {
$abundantNumbers[] = $i;
}
}
$countOfAbundantNumbers = count($abundantNumbers);
for ($j = 0; $j < $countOfAbundantNumbers; $j++) {
if (($j * 2) > $limit)
break; //if sum of two abundant exceeds limit ignore that
for ($k = $j; $k < $countOfAbundantNumbers; $k++) { //set $k = $j to avoid duble addtion like 1+2, 2+1
$l = $abundantNumbers[$j] + $abundantNumbers[$k];
$sumOfTwoAbundantNumbers[] = $l;
}
}
$numbers = array_diff($totalNumbers, $sumOfTwoAbundantNumbers);
echo '<pre>';print_r(array_sum($numbers));
$time_end = microtime(true);
$execution_time = ($time_end - $time_start);
//execution time of the script
echo '<br /><b>Total Execution Time:</b> ' . $execution_time . 'seconds';
exit;
function isAbundant($n) {
if ($n % 12 == 0 || $n % 945 == 0) { //first even and odd abundant number. a multiple of abundant number is also abundant
return true;
}
$k = round(sqrt($n));
$sum = 1;
if ($n >= 1 && $n <= 28123) {
for ($i = 2; $i <= $k; $i++) {
if ($n % $i == 0)
$sum+= $i + ( $n / $i);
if ($n / $i == $i) {
$sum = $sum - $i;
}
}
}
return $sum > $n;
}
I have a working solution for my problem but now I want to improve it.
Consider the array
3,4,5,9,1,2,8
I need to find the max difference between two elements at position i and j such that i < j that is I want to find max difference between two elements where the 2nd element comes after 1st element.
In the input I gave the answer is 7 because 8-1 = 7 and 8 is after 1.
The program works but when I have a very large array it takes lot of time. Can we improve on it?
function fMax($arr)
{
$sum = $arr[1] - $arr[0];
for($i=0;$i<count($arr);$i++)
{
for($j=$i+1;$j<count($arr);$j++)
{
if($sum < $arr[$j] - $arr[$i])
{
$sum = $arr[$j] - $arr[$i];
}
}
}
return $sum;
}
Thanks a lot to all the answers. I have used the code by codeaddict and it works fast.
Your current approach is O(N^2) you can improve it to O(N).
You are comparing each element with every other element. Instead you can keep track of the max difference and min element seen so far.
So every time you test a new element you see
if its difference with the current
min will give a better max sum if so
update the max sum and
if that number is less than the min
so far you update the min.
PHP function:
function ImprovedFindMax($arr) {
// initial max sum.
$sum = $arr[1] - $arr[0];
// initial min.
$min = $arr[0];
// iterate for every other ele starting from 2nd.
for($i=1;$i<count($arr);$i++) {
// if this ele give larger sum then update sum.
if($arr[$i] - $min > $sum) {
$sum = $arr[$i] - $min;
}
// if this ele is smaller than min..update min.
if($arr[$i] < $min) {
$min = $arr[$i];
}
}
// return $sum which will be the max sum.
return $sum;
}
Ideone Link
One iteration, track the minimum and the maxdiff. At each element, if the value is less than the minimum, set the minimum to the value; else, if the value - minimum is greater than maxdiff, set the maxdiff to that difference. Turns it from an O(n^2) to O(n).
This should work. I haven't tested it.
$arr = array(3,4,5,9,1,2,8);
$min = PHP_INT_MAX;
$maxdiff = 0;
foreach($arr as $i) {
if ($i < $min) {
$min = $i;
}
if ($maxdiff < $i - $min) {
$maxdiff = $i - $min;
}
}
echo "maxdiff: {$maxdiff}\n";