查找范围内的自我产品编号

时间:2013-03-29 01:24:12

标签: algorithm numbers product brute-force factorization

问题出在标题中。现在让自我产品这个词更加清晰。自我产品是指数字和数字的乘积。例如:

自我产品(1234)= 1234 * 1 * 2 * 3 * 4 = 29616。

我尝试了两种方法。

蛮力

任何人的想法都是检查1和N之间的所有组合(范围的上限)。并检查数字的自我产品是否在该范围内。这对于相对较低的数字来说是理想的,但是考虑到范围可能大到10 ^ 20,它会成为一个问题,因为它在打印结果之前需要一段时间。

分解

另一个想法是将范围内的数字分解。如果范围在60 000到70 000之间,当我检查62 688时,我将得到62 688为2 * 2 * 2 * 2 * 2 * 3 * 653。现在知道653不能成为一个数字,这意味着它必须是原始数字。然后我必须结合62 688的所有因素得到正确的答案,它应该输出这个是2612的自我产品,因为62 688 = 2612 * 2 * 6 * 1 * 2.

在这两种情况下,我都面临一个很大的问题,即检查所有组合。

P.S我发现如果数字有n位数,那么如果它的某个数字的自我产品比那个数字至少有n / 2位且不超过n。这将使我需要检查的数字列表略微缩小,但它并没有解决问题。

2 个答案:

答案 0 :(得分:1)

<强>命名

sp(x) = Self-Product(x) = y
Range = a to b

备注:

我假设您只是想要计数,但是扩展它以实际打印所有数字应该不难。

基本思路 - 将x拆分为具有某些数字前缀和一定数量的数字,然后确定某些数字+前缀是否完全在该范围内(添加可能性的数量) ,易于计算),部分(检查更长的前缀)或根本没有(沿着移动)。

请注意,不允许包含0的任何数字x,因为y = 0。

算法可能会有一些低效率,但它应该提供一个不错的起点。一个改进是应该能够进行一些二进制搜索过程。另一个不是一直重新计算最小值/最大值,因为这里有一定量的冗余。

<强>符号:
min(c,d) - 最小为c位且前缀为d(min(c,)表示无前缀)
max(c,d) - 最多为c位且前缀为d
range(c,d) - 范围从min(c,d)max(c,d)

请注意min(x,) = min(x,1) < min(x,2) < min(x,3) < ...
max(x,) = max(x,9) > max(x,8) > max(x,7) > ...

min(c,d..e) - 带有c位的最小值集和从d到e的任何前缀 (min(c,d)min(c,e)之间) max(c,d..e) - 带有c位的最大值集和从d到e的任何前缀 (在max(c,d)max(c,e)之间)

算法通过示例:

我将使用Range = 100到200的示例进行解释。

// check 1 digit
min(1,): x = 1, y = 1*1 = 1 < 100
max(1,): x = 9, y = 9*9 = 81 < 100
  // not in range, nothing to do

// check 2 digits
min(2,): x = 11, y = 11*1*1 = 11 < 100
max(2,): x = 99, y = 99*9*9 = 8019 > 200
  // partially in range, check longer prefix
  min(2,1): x = 11, y = 11*1*1 = 11 < 100
  max(2,1): x = 19, y = 19*1*9 = 171 > 100
    // partially in range, check longer prefix
    sp(11..16) <= sp(16) = 96 < 100
    sp(17) = 119 // in range - increment count
    sp(18) = 144 // in range - increment count
    sp(19) = 171 // in range - increment count
  min(2,2): x = 21, y = 21*2*1 = 42 < 100
  max(2,2): x = 29, y = 29*2*9 = 522 > 200
    // partially in range, check longer prefix
    // others outside of range, omitted for brevity
    sp(23) = 138 // in range - increment count
    sp(24) = 192 // in range - increment count
  min(2,3): x = 31, y = 31*3*1 = 93 < 100
  max(2,3): x = 39, y = 39*3*9 = 1053 > 200
    // partially in range, check longer prefix
    // others outside of range, omitted for brevity
    sp(32) = 192 // in range - increment count
  min(2,4): x = 41, y = 41*4*1 = 164 < 200
  max(2,4): x = 49, y = 49*4*9 = 1764 > 200
    // partially in range, check longer prefix
    // others outside of range, omitted for brevity
    sp(41) = 164 // in range - increment count
  min(2,5): x = 51, y = 51*5*1 = 255 > 200
    // not in range, nothing to do
  // no need to process (2,6..9), since these are all > min(2,5) > 200

// check 3 digits
min(3,): x = 111, y = 111*1*1*1 = 111 < 200
max(3,): x = 999, y = 999*9*9*9 = 728271 > 200
  // partially in range, check longer prefix
  min(3,1): x = 111, y = 111*1*1*1 = 111 < 200
  max(3,1): x = 199, y = 199*1*9*9 = 16119 > 200
    // partially in range, check longer prefix
    min(3,11): x = 111, y = 111*1*1*1 = 111 < 200
    max(3,11): x = 119, y = 119*1*1*9 = 1071 > 200
      // partially in range, check longer prefix
      // others outside of range, omitted for brevity
      sp(111) = 111 // in range - increment count
    min(3,12): x = 121, y = 121*1*2*1 = 242 > 200
      // not in range, nothing to do
    // no need to process (3,13..19), since these are all > min(3,12) > 200
  min(3,2): x = 211, y = 211*2*1*1 = 411 > 200
    // not in range, nothing to do
  // no need to process (3,3..9), since these are all > min(3,2) > 200

// check 4 digits
min(4,): x = 1111, y = 1111*1*1*1 = 1111 > 200
  // not in range, nothing to do
// no need to check more digits, since min(n,) > min(n-1,) and min(4,) > 200

因此,总计数为8个自产品,产生的范围为100到200.

请注意,有时我会检查范围的开始,有时候检查结束。这只是为了说明在特定情况下哪些条件很重要。

为了说明一些事情,如果范围是1-200,则具有前缀1的2位数字的范围将如上所述为[11,171],并且1 <= 11 <= 171 <= 200 ,所以我们可以只包括前缀为1的所有2位数字,其中有9个。

<强>实施

我写了一个基本实现的Java程序。对于60000到100000000000,它需要不到一秒,对于更大的数字(甚至在这个范围内),因为实现,会有算术溢出,所以所用的时间不能真正被信任(除此之外,我希望它应该只需要更长时间(对于此范围),而不是更短)。

final static long[] tenPowers = {1, 10, 100, 1000, 10000, 100000, 1000000,
  10000000, 100000000, 1000000000, 10000000000l, 100000000000l,
  1000000000000l, 10000000000000l, 100000000000000l, 1000000000000000l,
  10000000000000000l, 100000000000000000l, 1000000000000000000l};

public static void main(String args[]) throws InterruptedException
{
  System.out.println("Count = "+selfProductCountRange(60000, 100000000000l));
}

static long selfProductCountRange(long s, long f)
{
  start = s;
  finish = f;
  long count = 0;
  for (len = 1; ; len++)
  {
     long temp = selfProductCount(0, 0);
     if (temp == -1)
        break;
     count += temp;
  }
  return count;
}

static long selfProduct(long num)
{
  // pretend 0s are 1s for simplicity in the rest of the code
  long selfProduct = num;
  while (num > 0)
  {
     selfProduct *= Math.max(num % 10, 1);
     num /= 10;
  }
  return selfProduct;
}

static long start, finish;
static int len;

static long selfProductCount(long prefix, int prefixLen)
{
  long max = selfProduct((prefix+1) * tenPowers[len - prefixLen] - 1);
  // overflow hack
  if (max < 0)
     max = finish+1;
  if (max < start)
     return 0;
  long min;
  if (prefixLen != 0)
     min = selfProduct(prefix * tenPowers[len - prefixLen]);
  else
     min = selfProduct(tenPowers[len-1]);
  if (min > finish)
     return -1;
  if (max <= finish && min >= start)
     return getPossibilities(prefixLen);
  long val = 0;
  for (int i = 1; i < 10; i++)
  {
     long temp = selfProductCount(prefix*10+i, prefixLen+1);
     if (temp == -1)
        break;
     val += temp;
  }
  return val;
}

static long getPossibilities(int prefixLen)
{
  return (long)Math.pow(9, len-prefixLen);
}

答案 1 :(得分:0)

所以我们说产品是由digitsnumber组成的。

只需枚举数字()并检查每个排列(形成数字),看看它是否在范围内。

明显的修剪是当数字和最小排列的乘积大于上限时,你返回。

我认为32位整数足够快。