Algorithm for 'good' number

2019-03-07 01:00发布

问题:

A give number x is 'good' if the sum of any two consecutive digit of the number x are between k and 2k. I need to find an algorithm that for a given number k and a given number n, find how many 'good' n-digit numbers exist.

I made an implementation for this in PHP, but the complexity is to big (i am searching for all those 'good' number and counting them, so the complexity is O(10^n)).

<?php
    $n = 5;
    $k = 5;

    $min = $k*1;
    $max = $k*2;
    $counter = 0;

    for ($i = pow(10, $n-1); $i<pow(10,$n); $i++)
    {
        $number = $i;
        $prev = $number % 10;
        $number = $number / 10;

        while($number >= 10)
        {
            $crnt = $number % 10;
            $number = $number / 10;
            if ( ($crnt+$prev) > $min AND ($crnt+$prev) < $max ) {
                echo "good number: $i\n";
                $counter++;
            }
            $prev = $crnt;
        }
    }

    echo "counter: ".$counter."\n";
?>

Can someone confirm me if this can be the solution:

n=100 // given
k=10  // given

counter = 0;

for(i=10; i<100; i++)
{
    if( (i/10)+(i%10) > k ) && ( (i/10)+(i%10) < 2*k )
        counter++;
}

total = counter^(n-1)

回答1:

All those calls to pow certainly won't be helping.

What you can do is make a mapping of all the two-digit numbers that are 'good'. Once you have your mapping, all you need to do is check that every pair of digits in your number is good. You can do this by successive division by 10 and modulo 100.

Something like this would do the trick, provided you don't give it a negative number, and assuming you've set up your $good array.

function isgood( $num ) {
    while( $num >= 100 && $good[$num%100] ) {
        $num /= 10;
    }
    return $good[$num%100];
}

The next most obvious thing to do is memoize larger sequences. This is a dynamic programming principle. We've already memoized small sequences by storing the 'goodness' of 2-digit sequences. But you could easily use those to generate sequences of 3, 4, 5, 6 digits... Whatever your available memory allows. Use the memos you already have in order to generate the sequences with one extra digit.

So, if you built up memoisation for up to 5-digit numbers, then you divide by 1000 every time, and get a great speedup.



回答2:

Finding the number of "not good numbers" with n digits and top digit d is a straightforward dynamic programming problem.

10^n is the number of "good numbers" plus "not good numbers".

I will give you no more help than that.



回答3:

Your algorithm counts how many 2 digit integers are NOT good. Then it returns this value to the power of n - 1. This should get you the number of n digit numbers that are NOT good. If you subtract this value from the total amount of n digit integers, you should get what you want. Or we could avoid doing this by changing the signs:

for($i=10; $i<100; $i++) {
 if( ($i/10) + ($i%10) > $k && ($i/10) + ($i%10) < 2*$k ) {
  $cnt++;
 }
}
$result = pow($cnt, $n-1);

This should get you the number of good n digit integers, but let's see if that's really the case.

Well, cnt will give the number of good 2 digit integers. So, on the first two positions, we can put any of these cnt:

0 1 2 3 4 5 ...
x y

Then, what about positions 1 and 2? Well, position 1 is fixed by the first placement.

0 1 2 3 4 5 ...
x y
  y z

So we have to prove that there are cnt possibilities for z, and I don't see why this should be the case, so I would say that the algorithm is wrong. Your algorithm will probably overcount.