【问题标题】:Checking whether two numbers are permutation of each other?检查两个数字是否相互排列?
【发布时间】:2011-03-14 05:51:19
【问题描述】:

给定两个数字 a, b 使得 1

例如 - 123 是 312 的有效排列

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

【问题讨论】:

  • 一个数字怎么可能是另一个数字的排列?我们在谈论以 10 为底的数字字符串吗?数字 1-4-1 与数字 141 不同。
  • 你也可以这样想。
  • 这本质上是一个字谜检查。

标签: c++ c performance


【解决方案1】:

这是作业吗?

计算每个数字出现的次数并比较它们,如果它们相同,则可以使用置换将一个数字转换为另一个数字。

【讨论】:

  • Artyom:听起来更像是 ICPC 这样的编程竞赛的开胃菜问题。
  • 这也经常出现在 Project Euler 问题中,例如#49.
【解决方案2】:

如果您指的是数字的字符(例如 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,它会告诉您它们是否具有相同的数字计数。

【讨论】:

  • 是否可以不进行任何类型的排序而做到这一点?
  • 我会说这已经类似于散列。每个数字根据其哈希值(使用普通哈希函数)存储到一个单独的存储桶中。
  • @paxdiablo,嘿,你用基数排序骗了 Ravi!
  • @Ravi:这既是排序又是散列。当您通过键位子集的函数对数组进行索引时,这就是散列。当函数保留顺序时,这是索引或基数排序的一种形式。对一组整数 O(N) 进行排序的最快方法是将它们用作表中的索引。
  • @paxdiablo “这很有效,因为它是一个 O(n) 算法,其中 n 是两个数字的位数。”或者,换个角度来看,数字本身就是 O(log n),对吧?因为 n 的位数是 (floor(log10(n)) + 1)... :)
【解决方案3】:

创建一个数组:

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)。

【讨论】:

  • 这里n中的O(n)是位数。许多人可能会称它为O(log x),其中x 是数字。没有办法在少于O(log x) 的时间内做到这一点,但实际上如果x 适合整数变量类型,O(log x) 是常量。无论如何,如果 n 很大(谈论整数字符串而不是单个 C 变量),肯定没有比 O(n) 更好的算法,因为您必须至少检查每个数字一次......
  • @R..:更重要的是,如果 n 是固定的,任何 O(f(n)) 都会变成 O(1)。
  • R,我相信那些人会误会。 Big-O 表示法是数据集 size (n) 的函数,而不是数据集 value (x) 的函数。我认为您是对的,O(n) 与您将要做的一样好,没有空间交易时间:-)
  • @paxdiablo 你是对的,严格来说,big-O 中的n 是输入大小,这里输入的是数字,而不是数字本身。然而,根据输入值x 对这类问题进行分类是有先例的。这通常在n 算法是超多项式时完成,但就x 而言它是多项式。这些算法,通常用于 NP 完全问题,据说在“伪多项式时间”(en.wikipedia.org/wiki/Pseudo-polynomial_time)中运行。所以我想通过类比我们可以说这个算法运行在“伪对数时间”。
  • @Tyler:虽然请注意 AKS 论文的标题是“PRIMES 在 P 中”,而不是“PRIMES 是伪多项式对数”;-) 我宁愿看到/说“O (涉及 X 的东西),其中 X 是……”,而不是依赖每个人都使用相同的术语,除非在学术环境中。
【解决方案4】:

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");

【讨论】:

  • 这就是为什么他们说 C 具有汇编语言的所有功能以及汇编语言的所有可读性:-)
  • +1 以获得最佳答案。数数是最佳的。排序很慢。
  • 顺便说一下,int c[10] = {0}; 也同样适用于初始化数组。您可以使用常量 0 数组来 memcmp 以更少的代码检查结果。 :-)
  • @R: 或使用find_if - 可能比使用 0-array 的 memcmp 代码更多,但更多。
  • @jamesdlin:计算数字是(主要部分)一种排序方法。但是,如果您实际上没有组装/输出排序后的数字,那么(1)您没有对它们进行排序,并且(2)您节省了一些时间。该时间是否可测量是另一个问题,但事实是这段代码ab 的数字进行排序。它确实有一个中间状态,可以很容易地输出a的排序数字。
【解决方案5】:

如果你能建立一个 80GB 的表,你总是可以做到的:

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

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

【讨论】:

  • 我认为你不需要 64 位的等价类足够少。我应该给你 -1 来写 int64(一些非标准的怪事......?!)而不是 int64_t(ISO C)......
  • @R..:天哪,很抱歉不标准(更不用说奇怪了)。比尔格禁止!无论如何,关于更少的等价类,你是对的。事实上,我只是进行了蛮力计数 - 92378。
【解决方案6】:

{已编辑以添加额外的测试)

假设你在数字领域,怎么样

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

【讨论】:

  • 不。请注意,例如 '0' ^ '3' == '1' ^ '2'。
  • 好主意,不过。如果数字和不同,则它们不相等。
【解决方案7】:

如果我从您的问题中正确理解,则排列是元素的组合,不会重复。因此,如果 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

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

我希望这会有所帮助。

【讨论】:

  • 您的解决方案是可能的解决方案之一,但与此问题中发布的其他解决方案相比效率不高。
  • 好吧,我的解决方案实际上并不是一个解决方案,而是一个指向解决方案或我们如何实现它的提示。因为我提倡通过自己的实践和他人的经验来学习。因此,如果我提供完整的解决方案,我将迫使他根本不要动脑筋。是的,肯定可以有一些计算优化。但优化只有在我们知道该做什么之后才会出现。
【解决方案8】:

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

代码首先检查数字是否具有相同的位数,然后通过位移将这些数字相加为总数。除了移位距离乘以因子 6。这使得较小的数字不可能与较大的数字组成相同的值。例如,一个 '9' 需要 64 次 '8' 才能匹配它的值,这显然是不可能的。

此代码假定非负输入。

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;
}

【讨论】:

    【解决方案9】:

    不确定您为什么不想排序,除非这是您的家庭作业的条件。对于任何偶然发现这个问题的人,他们只是在寻找最快(也是最 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 非常有效。

    【讨论】:

    • 这个问题是关于 C 而不是 python。此外,其他答案中显示的哈希/基数排序比标准排序更快。
    【解决方案10】:

    您可以简单地计算数字的平方和: 123 = (1 * 1) + (2 * 2) + (3 * 3) 并将它们一起比较。如果它们的平方和相同,则它们是彼此的有效排列。

    【讨论】:

    • 84117522 具有相同的平方和 (82)? (64 + 16 + 1 + 1 == 49 + 25 + 4 + 4)
    猜你喜欢
    • 2021-10-07
    • 2017-02-02
    • 2017-02-09
    • 2013-09-23
    • 2017-11-30
    • 2021-06-21
    • 1970-01-01
    • 2023-03-03
    • 1970-01-01
    相关资源
    最近更新 更多