【问题标题】:how many n digit numbers are there with product p产品 p 有多少个 n 位数字
【发布时间】:2013-01-07 08:25:18
【问题描述】:

我们应该使用什么算法来获得n位数字的计数,使得其数字的乘积是p;这里的特殊条件是所有数字都不应该是1;

到目前为止,我的想法是对 p 进行素数分解。假设 n=3 和 p=24。

我们首先对 24 进行素数分解得到:2*2*2*3。

现在我在确定这些的组合时遇到了问题

4*2*3 , 2*4*3, .... 等 即使可以这样做......我将如何缩放 n 远小于素数。

我不太确定这是否是正确的方向...欢迎任何意见。

【问题讨论】:

    标签: algorithm combinatorics


    【解决方案1】:

    首先,您实际上并不需要完整的素数分解,只需分解为小于基数的素数(我猜您的意思是 10,但问题可以推广到任何基数)。所以我们只需要分解成前 4 个素数:2、3、5 和 7。如果其余(素数或非素数)因子大于 1,则问题有 0 个解。

    现在,假设将数字 p 考虑在内:

    p = 2^d1 * 3^d2 * 5^d3 * 7^d4
    

    也由n 数字组成:

    p =d(n-1)d(n-2)...d2d1d0

    然后,重新排列数字,也将是:

    p = 2^q2 * 3^q3 * 4^q4 * 5^q3 * ... * 9^q9
    

    qi >= 0q2 + q3 + ... q9 = n 的位置

    还有(由于分解):

    for prime=2:  d1 = q2      + 2*q4      + q6      + 3*q8
    for prime=3:  d2 =      q3             + q6             + 2*q9
    for prime=5:  d3 =                  q5
    for prime=7:  d4 =                            q7
    

    所以q5q7 是固定的,我们必须找到方程的所有非负整数解:
    (其余的都是未知数qi:q2, q3, q4, q6, q8 and q9

             d1 = q2      + 2*q4 + q6 + 3*q8
             d2 =      q3        + q6        + 2*q9
    n - d3 - d4 = q2 + q3 +   q4 + q6 +   q8 +   q9
    

    以上每一种解法,都有数位重排,可通过公式求出:

    X = n! / ( q2! * q3! * ... q9! )
    

    需要总结一下。

    这可能有一个封闭的公式,使用生成函数,您可以将其发布在 Math.SE


    p=24n=3 的示例:

    p = 2^3 * 3^1 * 5^0 * 7^0
    

    我们有:

    d1=3, d2=1, d3=0, d4=0
    

    整数解:

    3 = q2      + 2*q4 + q6 + 3*q8
    1 =      q3        + q6        + 2*q9
    3 = q2 + q3 +   q4 + q6 +   q8 +   q9
    

    (q2, q3, q4, q6, q8, q9) =:

    (2, 0, 0, 1, 0, 0) 
    (1, 1, 1, 0, 0, 0)
    

    给:

    3! / ( 2! * 1! )      = 3
    3! / ( 1! * 1! * 1! ) = 6
    

    3+6 = 9 的全部解决方案。


    p=3628800n=10 的示例:

    p = 2^8 * 3^4 * 5^1 * 7^1
    

    我们有:

    d1=8, d2=4, d3=1, d4=1
    

    整数解:

    8 = q2      + 2*q4 + q6 + 3*q8
    4 =      q3        + q6        + 2*q9
    8 = q2 + q3 +   q4 + q6 +   q8 +   q9
    

    (q2, q3, q4, q6, q8, q9)(连同相应的数字和每个解决方案的重新排列):

    (5, 0, 0, 0, 1, 2)    22222899 57    10! / (5! 2!)       =  15120
    (4, 0, 2, 0, 0, 2)    22224499 57    10! / (4! 2! 2!)    =  37800
    (4, 1, 0, 1, 1, 1)    22223689 57    10! / (4!)          = 151200
    (3, 2, 1, 0, 1, 1)    22233489 57    10! / (3! 2!)       = 302400
    (4, 0, 1, 2, 0, 1)    22224669 57    10! / (4! 2!)       =  75600
    (3, 1, 2, 1, 0, 1)    22234469 57    10! / (3! 2!)       = 302400
    (2, 2, 3, 0, 0, 1)    22334449 57    10! / (3! 2! 2!)    = 151200
    (2, 4, 0, 0, 2, 0)    22333388 57    10! / (4! 2! 2!)    =  37800
    (3, 2, 0, 2, 1, 0)    22233668 57    10! / (3! 2! 2!)    = 151200
    (2, 3, 1, 1, 1, 0)    22333468 57    10! / (3! 2!)       = 302400
    (1, 4, 2, 0, 1, 0)    23333448 57    10! / (4! 2!)       =  75600
    (4, 0, 0, 4, 0, 0)    22226666 57    10! / (4! 4!)       =   6300
    (3, 1, 1, 3, 0, 0)    22234666 57    10! / (3! 3!)       = 100800
    (2, 2, 2, 2, 0, 0)    22334466 57    10! / (2! 2! 2! 2!) = 226800
    (1, 3, 3, 1, 0, 0)    23334446 57    10! / (3! 3!)       = 100800
    (0, 4, 4, 0, 0, 0)    33334444 57    10! / (4! 4!)       =   6300
    

    这是2043720的全部解决方案,如果我没有犯任何错误的话..

    【讨论】:

    • 为此 +1。顺便说一句is this just useful or brilliant?为了获得一个伟大的功能:)
    • 不错的解决方案。但是你能告诉以编程方式找到树方程解的方法吗?
    【解决方案2】:

    我不认为我会从解决已知的“困难”问题开始,即计算素数分解。 我不认为我的意思是我的直觉,而不是任何对复杂性的严格计算,告诉我

    由于您最终只对p 的个位数除数感兴趣,因此我先将p 除以2,然后除以3,然后再除以4,一直到@ 987654326@。当然,其中一些除法不会产生整数结果,在这种情况下,您可以放弃该数字,不再考虑。

    对于p = 24 的示例,您将得到{{2},12}, {{3},8}, {{4},6}, {{6},4}, {{8},3}(即除数和余数的元组)。现在再次应用该方法,尽管这次您正在寻找其数字乘以余数的 2 位数字。也就是说,对于{{2},12},您将得到{{2,2},6},{{2,3},4},{{2,4},3},{{2,6},2}。碰巧所有这些结果都提供了 3 位数字,其数字乘以 24,但一般情况下,一些余数可能仍然有 2 位或更多位,您需要在这些位置修剪搜索树点。现在返回{{3},8} 继续。

    请注意,这种方法避免了必须单独计算需要考虑的一组数字的多少排列,因为它会枚举所有数字。它还避免了将2*24 视为单独的候选对象。

    我希望您也可以通过一些记忆来加快速度。

    现在我期待有更多组合学知识的人告诉我们这个问题的封闭式解决方案。

    【讨论】:

    • 他不需要完整的素数分解,只需分解为小于 10 的素数(2、3、5 和 7)。如果其余(质数或非质数)因子大于 1,则该问题有 0 个解。
    • @Asiri:如果数字以 14 为基数(或更大)并且您有一个值为 13 的数字,这将起作用。在 base-10 中,没有这样的数字。
    • @ypercube:我的错,我读错了问题。感谢您的澄清。
    • @Mark:{{3},8} 不会重新生成一些已经从{{2},12} 计算的解吗?
    • @AsiriRathnayake: 不,所有来自{{3},8} 的数字都以数字3 开头,所有来自{{2},12} 的数字都以2 开头——我相信我的方法计算了所有一组 3 位数字的有效排列,无需明确排列任何数字组。
    【解决方案3】:

    您可以使用基于以下公式的动态规划方法:

    f[ n ][ p ] = 9 * ( 10^(n-1) - 9^(n-1) ),  if p = 0
                  0,  if n = 1 and p >= 10
                  1,  if n = 1 and p < 10
                  sum{ f[ n - 1 ][ p / k ] for 0 < k < 10, p mod k = 0 }, if n > 1 
    

    第一种情况是 p = 0 的单独情况。这种情况在 O(1) 中计算,此外有助于从第 4 种情况中排除 k = 0 值。
    2nd3rd 案例是动态基础。
    4th 案例 k 依次取最后一位数字的所有可能值,我们将数字的数量与乘积 p 与最后一位数字 相加>k 通过减少到较小尺寸的相同问题。

    如果您使用记忆实现 dp,这将有 O( n * p ) 运行时间。

    PS:我的回答是针对比 OP 描述的更普遍的问题。如果必须满足没有数字必须等于1的条件,公式可以调整如下:

    f[ n ][ p ] = 8 * ( 9^(n-1) - 8^(n-1) ),  if p = 0
                  0,  if n = 1 and p >= 10 or p = 1
                  1,  if n = 1 and 1 < p < 10
                  sum{ f[ n - 1 ][ p / k ] for 1 < k < 10, p mod k = 0 }, if n > 1 
    

    【讨论】:

    • 我同意一般的想法。但我没有得到p = 0 的情况。比如说p = 0 &amp; n = 1,这些等式会给出答案9 * 1
    • @AsiriRathnayake:谢谢,我的错,必须有 n - 1 的幂。现在更正。
    • @GrigorGevorgyan:如果p = 0 &amp; n = 2,我们仍然会得到9 作为结果。 TBH 我不明白为什么我们需要p = 0 案例。可能有p &gt; 1 作为先决条件......并让1 &lt; k &lt; 10。这是因为 OP 提到任何数字都不应该是1
    • @AsiriRathnayake: 如果p = 0 &amp; n = 2 答案确实是 9。数字是 10、20、30、...、90。我发布了针对一般情况的解决方案,没有关于数字不相等的条件到 1. 如有必要,可以针对这种情况轻松调整公式。我会为此写一个PS。
    • @GrigorGevorgyan:好吧,我完全错过了这种可能性。 +1。
    【解决方案4】:

    对于N个数字,其数字的乘积是p;

    例如,如果 n = 3 且 p =24

    排列如下(排列)

    = (p!)/(p-n)!
    = (24!) /(24 -3)!
    = (24 * 23 * 22 * 21 )! / 21 !
    = (24 * 23 * 22 )
    = 12144
    

    这样就可以安排12144了

    组合如下

    = (p!)/(n!) * (p-n)!
    = (24!) /(3!) * (24 -3)!
    = (24 * 23 * 22 * 21 )! / (3!) * 21 !
    = (24 * 23 * 22 ) / 6
    = 2024
    

    希望对你有帮助

    【讨论】:

    • 我认为你需要更清楚地解释这个答案是如何解决问题的,如果确实如此的话。
    • 您计算了将 24 个实体放入 3 个位置的排列和组合,这不是问题所要求的。我只计算 p = 24, n = 3 的 10 个排列。
    • 我数到 9:234, 243, 324, 342, 423, 432, 226, 262, 622
    【解决方案5】:

    这些问题似乎是人为的,但无论如何你所看到的都有上限。例如 p 不能有大于 7 的素数除数,因为它必须是单个数字(“使得其数字的乘积”)。

    因此假设 p = 1 * 2^a * 3^b * 5^c * 7^d。

    2^a 可以来自 ceil(a/3) 到 'a' 数字。 3^b 可以来自 ceil(b/2) 到 'b' 位。 5^c 和 7^d 可以分别来自 'c' 和 'd' 数字。其余数字可以用 1 填充。

    因此 n 的范围可以从 ceil(a/3)+ceil(b/2)+c+d 到无穷大,而 p 有一组固定值。

    【讨论】:

      【解决方案6】:

      质数分解感觉是正确的方向,尽管您不需要任何大于 7 的质数,因此您可以反复除以 2、3、5、7。 (如果我们没有得到一个素数,或者得到一个 > 7,则没有解决方案。

      一旦我们有了质因数,p % xp / x 就可以实现为常数时间操作(实际上不需要p,您可以只保留质因数)。

      我的想法是,用下面的算法计算组合,从那里得到排列很容易。

      getCombinations(map<int, int> primeCounts, int numSoFar, string str)
        if (numSoFar == n)
          if (primeCounts == allZeroes)
            addCombination(str);
          else
            ;// do nothing, too many digits
        else if (primeCounts[7] >= 1) // p % 7
          getCombinations(primeCounts - [7]->1, numSoFar-1, str + "7")
        else if (primeCounts[5] >= 1) // p % 5
          getCombinations(primeCounts - [5]->1, numSoFar-1, str + "5")
        else if (primeCounts[3] >= 2) // p % 9
          getCombinations(primeCounts - [3]->2, numSoFar-1, str + "9")
          getCombinations(primeCounts - [3]->2, numSoFar-2, str + "33")
        else if (primeCounts[2] >= 3) // p % 8
          getCombinations(primeCounts - [2]->3, numSoFar-1, str + "8")
          getCombinations(primeCounts - [2]->3, numSoFar-2, str + "24")
          getCombinations(primeCounts - [2]->3, numSoFar-3, str + "222")
        else if (primeCounts[3] >= 1 && primeCounts[2] >= 1) // p % 6
          getCombinations(primeCounts - {[2]->1,[3]->1}, numSoFar-1, str + "6")
          getCombinations(primeCounts - {[2]->1,[3]->1}, numSoFar-2, str + "23")
        else if (primeCounts[2] >= 2) // p % 4
          getCombinations(primeCounts - [2]->2, numSoFar-1, str + "4")
          getCombinations(primeCounts - [2]->2, numSoFar-2, str + "22")
        else if (primeCounts[3] >= 1) // p % 3
          getCombinations(primeCounts - [3]->1, numSoFar-1, str + "3")
        else if (primeCounts[2] >= 1) // p % 2
          getCombinations(primeCounts - [2]->1, numSoFar-1, str + "2")
        else ;// do nothing, too few digits
      

      考虑到事情的完成顺序,我认为不会有重复。

      改进:

      一旦你查看了p%5,你就不需要再查看p%7(在堆栈的更深处),因为我们知道它不能再被7整除,所以很多这些检查都可以被优化掉。

      primeCounts不必是map,可以是长度为4的数组,不需要复制,可以适当增减数值即可。 str 也可以做类似的事情(字符数组)。

      如果getCombinations(..., str + "8") 的位数过多,则检查"24""222" 是没有意义的。这种和类似的检查应该不会太难实现(只需让函数返回一个布尔值)。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-10-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-03-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多