最小的倍数,只有0和4作为其数字

时间:2015-08-17 16:50:43

标签: c++ algorithm

给定数字A找到最小的数字B,使得A * B仅包含数字4和0,而零只应在最后,即像404这样的数字无效。

例如:

| A | B   | A*B |
|---|-----|-----|
| 1 | 4   | 4   |
| 2 | 2   | 4   |
| 3 | 148 | 444 |
| 4 | 1   | 4   |
| 5 | 8   | 40  |

好吧,我以这种方式尝试了这个问题。保持整数队列。最小的数字是4.

Pop the number (i.e. the front element of the queue) and push the numbers that can be derived from this popped number. 
That is , when we pop 4, we push (4*10) = 40 and (4*10 + 4) = 44 with the constraint that the popped number is not divisible by 10. If it is, push only its next multiple of 10.

所以,我的队列将是:4,40,44,400,440,444,......

当我从队列中弹出元素时,我将检查它是否可以被给定的数字A整除。如果是,只需中断并弹出数字就是我想要的结果。

因为我的号码真的很大,所以我保留了pair<string,int>的队列,其中string对应number,int对应remainder。因此,可以很容易地计算下一阶段的剩余部分。

前:

queue : <"4",4> 
Pop , current result is string : "4" and remainder is lets say prev = 4
check if the last digit is 0 or not (for checking if its a multiple of 10 or not)
If not, then append 0 to this string and remainder as (prev*10)%a and push this pair in the queue. Also, append 4 to this string with remainder as : (prev*10 +4)%a and push. If the last digits is 0, append 0(not 4) and corresponding remainder, push this pair in the queue.

Queue: <"40",(prev*10)%a>, <"44", (prev*10 +4)%a> and so on..

直到队列前面的对为余数0,我们将继续这样做。

虽然这种改进似乎有点好,但不正确并没有通过所有测试用例。有人可以说明如何以最佳方式解决这个问题。 (A的范围是10 ^ 10)。

3 个答案:

答案 0 :(得分:11)

如果我理解了问题,解决方案必须与regualr表达式0 | 4 + 0 *

匹配

它花了很多时间我不用C编程,但是下面的代码可以解决问题。

int calc( int n ) {
  int factor5;
  int factor2;
  int j;
  int a;
  int b;
  int i;

  /* trivial case 0 result is 0 */
  if( n==0 ) return 0;

  /* find the number of factors 2 and 5 in n */
  for( a=n, factor5=0; a%5==0; a/=5, factor5++ ); 
  for( a=n, factor2=0; a%2==0; a/=2, factor2++ ); 

  /* result is r=b*a where a=2^(j+2)*5^j */
  j=factor5;
  if( j < factor2-2 ) j=factor2-2; 

  for( a=4,i=0; i<j; i++, a*=10 ); 

  /* generate b in 1, 11, 111, ... until solution found */ 
  for( b=1; (a*b)%n!=0; b=10*b+1 );
  return a*b;
}

可以通过以下方式进行测试:

int main ( void ) {
  int n,r;

  for( n=0; n<10; n++) {
    r=calc(n); printf( "n=%d r=%d\n", n, r );
  }

  return 0;
}

注意:优化它。 Alsochange“int”to“long”,“long long”或使用任意长度的图书管理员。

试验:

n=0 r=0
n=1 r=4
n=2 r=4
n=3 r=444
n=4 r=4
n=5 r=40
n=6 r=444
n=7 r=444444
n=8 r=40
n=9 r=444444444

<强> Rationalle:

除了具有结果0的普通情况0之外,结果“r”必须匹配正则表达式“4 + 0 *”:fours后跟零。也就是说,在正常的算术中,r = x * 10 ^ j,其中x在4,44,444,....

如果我们提取因子4,我们在序列1,11,111,...中使用x得到r = x * 4 * 10 ^ j。注意,这个序列中的数字从不是因子2和5(它们不是偶数,也不是0或5)。

总之,r = x * 2 ^(j + 2)* 5 ^ j,其中x为1,11,111,...和“j”,从参数的因式分解中获得。程序的第一步是计算“j”,接下来计算a = 2 ^(j + 2)* 5 ^ j,最后生成序列1,11,111,...并测试它直到第一个有效结果。

答案 1 :(得分:4)

让我们拨打40个号码C,即C = A x B

根据我理解的限制条件,这会使语言C = AxB中的4^n 0^m(请勿将此作为4读取为权力n,这是为了表示重复4 n),因此我们只需要nm来描述C

<强>观察:

  1. 找到最小的B相当于找到C倍数的最小A
  2. 如果两个C - 数字的位数不同,则位数较高的C会更大。
  3. 当两个C - 数字的位数相等n + m时,C的{​​{1}}数越大越好。
  4. 因此,我们循环使用4(以C开头并增加)和14的数字位数来固定数字数字,再次从单个C开始并增加。这为数字顺序提供了所有可能的4 - 数字。

    如前所述并在@pasaba por aqui的回答中使用,可以通过利用CA可能共享素数因子的事实来减少搜索空间。在这种情况下,每个C始终至少有素因子C2),可能还有2^2 = 45)。

    确实,2*5 = 10C = x * 2^(j + 2) * 5^j(即x in [1, 11, 111, ...])。最小C = x * 4 * 10^j等于j25个因素的最高数量。例如。如果AA % 25 == 0需要j2A % 400 == 0需要j,依此类推。

    请参阅pasaba por aqui对该解决方案的回答。

    蛮力版本:

    4

答案 2 :(得分:4)

这里我给出了一个有效的算法,即在A的值中及时工作(编辑:校正)多项式以解决数字A的问题。我还给出了一个证据,证明所有数字A和证明我的算法的正确性 - 证据表明,对于任何数字A,正确的答案最多有A ^ 2 4(并且零的数量最多是A的数字的两倍,粗略地) 。 (我不知道A ^ 2四肢是否是最好的界限,但我认为它可能并不太远。)运行时间不会超过A或输出的多项式。 (我没有完全解决。)现在看到其他答案,它与pasaba por aqui的答案基本相同,但我想我会更加严谨地解释它为何起作用。 (虽然我的写作可能会得到改善......)

术语:在续集中,我将说a number N has the form {STRING}表示它的十进制扩展与正则表达式{STRING}匹配,即使这不是标准的数论术语。

问题:给定A,找到形式为“4 + 0 *”的最小整数N,使得N mod A = 0

步骤1:考虑10 mod A,特别是n = 1,2,3的序列{10 ^ n mod A},...

第一个显而易见的问题是,如果10是可逆的模式A,即10与A相互作用会发生什么,如果不相反则相反。 (编辑:这实际上并不明显,但在这些基本数论的90%中,取得进展的方法是根据所涉及的数字的主要因素分析进行​​一些案例分析,并思考何时事物是互质的,何时是他们共同的共同因素往往是一个很好的方向。)

如果10与A不相互作用,则有一些可能性。一个是10分A,这是一个愚蠢的案例。我们可以简单地将A除以10,然后找到答案,然后将其乘以10.如果排除了,那么5除以A,但是2除以A,或2除以A但是5除以A,或者A与10相除

假设5除以A,但2则不对。如果N mod A = 0具有上述形式,则考虑N mod 5 - 它等于自5 |以来的最低位数因此,最低位数必须为0而不是4,所以10 | N.也就是说,在这种情况下,形式为“4 + 0 *”的任何整数,使得N mod A = 0,也具有N mod 2A = 0.并且10除以2A,这意味着我们可以减少到更简单的问题

假设2除以A,但5则不对。很明显,4实际上除了任何数量的形式“4 + 0 *”,因此对于任何奇数A',所描述的最小整数N是相同的,无论我们将A取为A',2A'还是4A ”。现在假设8除以A.由于8除以任何数字的形式“40+”,而8不划分4,通过与之前类似的参数暗示数字N必须具有零作为其低位,所以如果8 | A,它意味着如果N mod A = 0,那么N mod 5A = 0.所以我们可以移动到这个数字,然后取出10的幂并减少到一个更简单的问题。

因此,我们可以将注意力限制在10与A相互作用的情况。

这简化了事情因为那时基本数论(中国余数定理)告诉我们10是可逆的模态A,即对于一些足够大的k,10 ^ k = 1 mod A.这也意味着我们可以忽略N的数字中零的可能性 - 因为如果X * 10 ^ y = 0 mod A,而10是可逆的mod A,我们还必须具有X = 0 mod A,这将是是一个较小的解决方案。

因此,一旦10与A相互作用,则形式为“4 + 0 *”的最小整数N使得N mod A = 0与形式“4+”的最小整数相同,使得N mod A = 0。

(另外,它现在很清楚,总是存在这种形式的SOME整数N可被A整除。所以所有这些程序确实终止并且不为任何输入无限循环:)因为,我们可以做到双赢分析。假设10 ^ k = 1 mod A.现在考虑由正好k 4组成的十进制数K的值,减去模数A.如果这是零,那么证明数字存在并且我们已经完成了。如果它不为零,则说它是某个值“a”mod A不等于0.我们知道数字K * 10 ^ k也等于“a”mod A,因为10 ^ k = 1 mod A.并且K * 10 ^ k也具有我们关心的形式,对于任何i,K * 10 ^ {ik}也是如此。因此,如果我们取一个由A * k 4组成的十进制数,它必须等于A * a = 0 mod A.因此,我们构造了一个可以被A整除的所需形式的数字N.)

现在我们可以通过一个简单的for循环直接解决这个问题。我们只是跟踪值“4000000 ... mod A”和值“444444 .... mod A”,其中数字是k位长,我们计算k + 1位数的模数值,将第一个值乘以10 mod A的值,减去模A,然后将其加到第二个并减少模数A.

这是完整的代码:

#include <cassert>
#include <iostream>

typedef unsigned long long ul;

ul fast_finder(ul A) {
  assert(A);
  ul num_zeros = 0; // remember how many zeros we need to add at the end

  while ((A % 10) == 0) {
    A /= 10;
    ++num_zeros;
  }

  while ((A % 5) == 0) {
    A /= 5;
    ++num_zeros;
  }

  while ((A % 8) == 0) {
    A /= 2;
    ++num_zeros;
  }

  while ((A % 2) == 0) {
    A /= 2;
  }

  ul four_mod_A = 4 % A;
  ul ten_mod_A = 10 % A;

  ul num_fours = 1;
  // in these variable names "k" is the number of fours we are considering
  ul four_times_ten_to_the_k_mod_A = four_mod_A;
  ul sum_of_fours_mod_A = four_mod_A;
  while (sum_of_fours_mod_A) {
    four_times_ten_to_the_k_mod_A *= 10;
    four_times_ten_to_the_k_mod_A %= A;
    sum_of_fours_mod_A += four_times_ten_to_the_k_mod_A;
    sum_of_fours_mod_A %= A;
    ++num_fours;
  }

  // now build an integer representation of the result from num_fours, num_zeros
  ul result = 0;
  while (num_fours) {
    result *= 10;
    result += 4;
    --num_fours;
  }
  while (num_zeros) {
    result *= 10;
    --num_zeros;
  }
  return result;
}

// This is to check the correctness of the fast algorithm, it's just the naive algorithm.
ul slow_finder(ul A) {
  assert(A);
  for (ul B = 1;;++B) {
    ul N = B * A;
    bool saw_four = false;
    while (N) {
      ul low = N % 10;
      if (low == 4) {
          saw_four = true;
      } else if (low != 0 || saw_four) { break; }
      N /= 10;
    }
    if (N == 0)
      return A*B;
  }
}

void check(ul x) {
  std::cout << x << ": ";
  ul f = fast_finder(x);
  std::cout << f << std::flush;
  ul s = slow_finder(x);
  if (f != s) {
    std::cout << "failed! ( " << s << " )" << std::endl; return;
  }
  std::cout << '.' << std::endl;
}

int main() {
  check(1);
  check(3);
  check(4);
  check(5);
  check(10);
  check(11);
  check(13);
  check(15);
  check(18);
  check(73);
  check(64);
  check(52);
}