检查两个数字是否相互排列?

时间:2010-07-10 11:59:28

标签: c++ c performance

给出两个数a,b使得1 <= a,b <= 10000000000(10 ^ 10)。我的问题是检查它们中的数字是否是彼此的排列。这样做的最快方法是什么?我想到使用散列但无法找到任何合适的散列函数。有什么建议吗?

例如 -     123是312的有效排列

我也不想对数字中的数字进行排序。

9 个答案:

答案 0 :(得分:31)

如果你的意思是数字的字符(例如1927和9721),那么(至少)有两种方法。

如果允许排序,一种方法是简单地sprintf将它们分配到两个缓冲区,对缓冲区中的字符进行排序,然后查看字符串是否相等。

但是,鉴于您希望对数字进行排序,另一种方法是设置一个十元素数组,所有元素最初设置为零,然后处理第一个数字中的每个数字,递增相关元素。

然后对第二个数字进行相同的操作,但递减。

如果,最后,它仍然是全零,则数字是彼此的排列。

这是有效的,因为它是O(n)算法,其中n是两个数字中的位数。这种野兽的伪代码类似于:

def arePermutations (num1, num2):
    create array count, ten elements, all zero.
    for each digit in num1:
        increment count[digit]
    for each digit in num2:
        decrement count[digit]
    for each item in count:
        if item is non-zero:
            return false
    return true

在C中,以下完整的程序说明了如何做到这一点:

#include <stdio.h>
#include <stdlib.h>

#define FALSE (1==0)
#define TRUE  (1==1)

int hasSameDigits (long num1, long num2) {
    int digits[10];
    int i;

    for (i = 0; i < 10; i++)      // Init all counts to zero.
        digits[i] = 0;

    while (num1 != 0) {           // Process all digits.
        digits[num1%10]++;        // Increment for least significant digit.
        num1 /= 10;               // Get next digit in sequence.
    }

    while (num2 != 0) {           // Same for num2 except decrement.
        digits[num2%10]--;
        num2 /= 10;
    }

    for (i = 0; i < 10; i++)
        if (digits[i] != 0)       // Any count different, not a permutation.
            return FALSE;

    return TRUE;                  // All count identical, was a permutation.
}

int main (int c, char *v[]) {
    long v1, v2;

    if (c != 3) {
        printf ("Usage: %s <number1> <number2>\n", v[0]);
        return 1;
    }

    v1 = atol (v[1]);
    v2 = atol (v[2]);
    if (hasSameDigits (v1, v2)) {
        printf ("%d and %d are permutations\n", v1, v2);
    } else {
        printf ("%d and %d are not permutations\n", v1, v2);
    }

    return 0;
}

简单地传递两个(正数)数字,并假设它们适合long,它会告诉您它们是否具有相同的数字计数。

答案 1 :(得分:17)

如果a和b具有相同数量的每个数字,则a和b是字谜。所以基本上最快的方式似乎是,计算a和b的数字:

int c[10]={0,0,0,0,0,0,0,0,0,0}

while (a) { c[a%10]++; a/=10; }
while (b) { c[b%10]--; b/=10; }

int res=1;
for (int i=0;i<10;i++) res &= c[i]==0;
printf(res?"yes":"no");

答案 2 :(得分:3)

是作业吗?

计算每个数字的出现次数并进行比较,如果它们相同则可以使用排列将一个数字转换为其他数字。

答案 3 :(得分:1)

创建一个数组:

int digitOccurances[2][10];

digitOccruances[X][N]存储数字N出现在数字X中的次数。因此,如果您将8675309与9568733进行比较,则阵列最终会显示为:

{ { 1, 0, 0, 1, 0, 1, 1, 1, 1, 1 } , { 0, 0, 0, 2, 0, 1, 1, 1, 1, 1 } }

如果两个数组相等,那么数字就是排列。

这是一个O(n)算法,所以渐渐地说这是最有效的算法(如果不至少检查一次所有数字就无法解决这个问题。

如果数字长度不同,您可以立即返回false,因此假设两者的长度均为n。填充数组需要2n次操作,然后完成10次比较才能读取数组。 2n + 10是O(n)。

答案 4 :(得分:1)

我在rossetacode.org找到了这个相当有效的解决方案。我希望你原谅我用Java编写它(我对C不太满意),但语法应该或多或少相同。

代码首先检查数字是否具有相同的位数,然后通过将它们转换为总数来对数字求和。除了移位距离乘以因子6.这使得较小的数字不可能与较大的数字组成相同的值。比如一个&#39; 9&#39;需要64次&#39; 8&#39;匹配它的价值,这显然是不可能的。

此代码假定为非负输入。

boolean haveSameDigits(long n1, long n2) {
    long nn1 = n1, nn2 = n2;
    while (nn1 > 0 && nn2 > 0) {
        nn1 /= 10;
        nn2 /= 10;
    }
    if (nn2 != nn1) // not the same length
        return false;

    long total1 = 0, total2 = 0;
    while (n1 != 0) {
        total1 += 1L << ((n1 % 10) * 6);
        total2 += 1L << ((n2 % 10) * 6);
        n1 /= 10;
        n2 /= 10;
    }
    return total1 == total2;
}

答案 5 :(得分:0)

如果您可以构建一个80GB的表,那么您可以随时执行:

int64 table[10000000000] = {0, blah blah..., 9999999999};

if (table[a] == table[b]) ...

答案 6 :(得分:0)

如果我从你的问题中正确理解了一个排列是元素的组合,这些元素不再重复。因此,如果123是312的有效排列,则

也是如此
123,
213,
132,
321,
213, 

等等。

基于这个假设,假设你有两个整数123456789和129837456.(为简单起见,我也假设两个数字的长度相等)。如果您理解了这一点,那么您也可以检查不同的排列和组合。

因为你需要做的就是从给定的数字中得到单位的整数,例如:

Number 123456789 is
1 * 100000000 + 
2 * 10000000  +
3 * 1000000   +
4 * 100000    +
5 * 10000     +
6 * 1000      +
7 * 100       +
8 * 10        +
9 

1 * power(10, 8) +
2 * power(10, 7) +
3 * power(10, 6) +
4 * power(10, 5) +
5 * power(10, 4) +
6 * power(10, 3) +
7 * power(10, 2) +
8 * power(10, 1) +
9 * power(10, 0) 

我确实给了你算法提示如何做到这一点,这样就可以轻松完成。一旦完成,您将最终得到单独的整数(更好地将这些值保存在数组中)

1, 2, 3, 4, 5, 6, 7, 8, 9

现在

对另一个给定的整数执行相同的操作,因此您将得到另一个整数数组

1, 2, 9, 8, 3, 7, 4, 5, 6

所以现在你需要检查的是,如果第二个数组的所有整数都存在于第一个整数数组中,如果是,则它们是第一个数组或第一个数字的整数的排列。

我希望这会有所帮助。

答案 7 :(得分:-1)

{已编辑添加其他测试)

假设您在数字域中,那么

if 
(
    ('1' ^ '2' ^ '3' == '3' ^ '1' ^ '2') &&
    ('1' + '2' + '3' == '3' + '1' + '2')
)
{
    cout << "Yes\n";
}
else
{
    cout << "No\n";
}

答案 8 :(得分:-1)

不确定为什么你不想排序,除非这是你的家庭作业的一个条件。对于任何绊倒这个问题的人,只是寻找最快(最pythonic!)的方式来测试两个整数是否是Python中的排列

def arePermutations(a, b):
    return sorted([d for d in str(a)]) == sorted([d for d in str(b)])

这个解决方案在Python中的运行速度稍快,当然,依赖于测试的数字是相对较小的整数。它对Project Euler问题52非常有效。