Ones

2013-05-19 Robert Zhang 更多博文 » 博客 » GitHub »

原文链接 http://huiming.io/2013/05/19/ones.html
注:以下为加速网络访问所做的原文缓存,经过重新格式化,可能存在格式方面的问题,或偶有遗漏信息,请以原文为准。


分析:比较简单的方案是:依次用1,11,111,1111,……来尝试,直到某个数与输入的余数为0为止。当然,111……1可能非常大(实际上可达数千位之巨,比如8141对应的111……1有3486位),为此我们要使用自制的“大数”来运算。据此有方案一:<!--more-->

#include <iostream>
#include <vector>

using namespace std;

typedef vector<char> BigNumber;

inline BigNumber bignum(int n) {
  BigNumber r;
  while (n) {
    r.push_back(n % 10);
    n /= 10;
  }
  return r;
}

inline void shift_add(BigNumber & n, char d) {
  n.push_back(0);
  for (int i = n.size() - 2; i >= 0; i--) {
    n[i + 1] = n[i];
  }
  n[0] = d;
}

inline bool less_than(const BigNumber & op1, const BigNumber & op2) {
  bool r = false;
  if (op1.size() != op2.size()) {
    r = op1.size() < op2.size();
  }
  else {
    for (int i = op1.size() - 1; i >= 0; i--) {
      if (op1[i] != op2[i]) {
        r = op1[i] < op2[i];
        break;
      }
    }
  }
  return r;
}

inline void subtract(BigNumber & op1, const BigNumber & op2) {
  int borrow = 0;
  int i;
  for (i = 0; i < op2.size(); i++) {
    char d = op1[i] - op2[i] - borrow;
    if (d < 0) {
      d += 10;
      borrow = 1;
    }
    else
      borrow = 0;
    op1[i] = d;
  }
  for (i = op2.size(); i < op1.size(); i++) {
    char d = op1[i] - borrow;
    if (d < 0) {
      d += 10;
      borrow = 1;
    }
    else
      borrow = 0;
    op1[i] = d;
  }
  while (op1.size() && !op1.back())
    op1.pop_back();
}

inline BigNumber first(const BigNumber & n) {
  BigNumber r(n.size(), 1);
  if (n.back() > 1)
    r.push_back(1);
  return r;
}

inline void increase(BigNumber & n) {
  n.push_back(1);
}

bool mod(const BigNumber & op1, const BigNumber & op2) {
  BigNumber r;
  for (int i = op1.size() - 1; i >= 0; i--) {
    shift_add(r, op1[i]);
    while (!less_than(r, op2)) {
      subtract(r, op2);
    }
  }
  return r.size();
}

int length(int n) {
  BigNumber bn = bignum(n);
  BigNumber t = first(bn);
  while (mod(t, bn)) {
    increase(t);
  }
  return t.size();
}

int main() {
  int n;
  while (cin >> n) {
    cout << length(n) << endl;
  }
  return 0;
}

这么做正确性没有问题,但是会超时。关键在于mod运算的效率太低。如何改进?

设f(x)为x与a的余数:

f(x) = x mod a

其中x=111...1,0 < a < 10000。则:

f(10x + 1) = (10x + 1) mod a = ((10x) mod a + 1 mod a) mod a

= (((10 mod a)(x mod a)) mod a + 1 mod a) mod a

= (((10 mod a)f(x)) mod a + 1 mod a) mod a

当a > 10时有:

f(10x + 1) = ((10 * f(x)) mod a + 1) mod a

从此可见:

1)计算较大的x与a的余数可以利用较小的x与a的余数

2)在这个计算过程中只要使用int类型就可以保存任何变量的值(11...1除外,但它本身不需保存),因此可以使用编程语言本身的mod运算,极大地提高运算速度。

3)当a < 10时,手工计算可以得出(l(x)为1的个数):

l(1) = 1, l(3) = 3, l(7) = 6, l(9) = 12

由此有方案二:

#include <iostream>
using namespace std;

char values[10] = {0, 1, 0, 3, 0, 0, 0, 6, 0, 9};

int length(int n) {
  int l;
  if (n > 10) {
    int r = 1;
    l = 1;
    while (r) {
      r = ((10 * r) % n + 1) % n;
      l++;
    }
  }
  else {
    l = values[n];
  }
  return l;
}

int main() {
  int n;
  while (cin >> n) {
    cout << length(n) << endl;
  }
  return 0;
}

真的太简单了!