【问题标题】:C: Detect duplicate integers in a long arrayC:检测长数组中的重复整数
【发布时间】:2013-05-30 21:45:34
【问题描述】:

我查看了关于 SO 和其他地方的各种类似问题,但我觉得有些特殊情况可能需要提出新问题。

这是问题:

我有一个整数数组,其中可以包含多达十亿个整数。这些数字将在 1 到 10 亿之间,但可能存在缺失值。所以每个值 32 位就足够了。我唯一想做的就是确保没有重复。当我发现第一次出现重复时,我大惊小怪并退出。这将在许多文件上完成,并且很少期望这些文件有重复。所以一般情况也经常是最坏的情况。

我知道如何在 shell 中很容易地做到这一点(在文本文件中,我将从以下位置读取整数:sort | uniq 等),这大约需要 13 秒。因此,希望纯 C 智能算法会做得更好。我的想法是我在数组上使用快速(希望很容易获得)排序并迭代每个连续对的计算差异。找到零的那一刻,我停下来退出。

这是一个玩具示例:

1001
1002
1003
1004
1005
1003
...

我首先对数组进行排序并得到: 1001 1002 1003 1003 1004 1005 ...

然后当我看到 line3 - line4 == 0 时,我停在第四行。

如果一切顺利,我会以退出代码 0 静默退出。

这些是我的要求/限制: 1) 我是 C 语言的初学者(我的代码只有 100 行)。 2) 我会非常喜欢纯 C 解决方案来学习。标准库没问题。 3) 如果 C++ 解决方案在减少编程时间方面非常出色,那么也请提出建议。

非常感谢。

【问题讨论】:

  • 这里的问题到底是什么?你熟悉吗? qsort()?
  • 整数的最大范围是多少,即你需要多少位来唯一地表示一个?
  • 您可以通过散列快速完成此操作,时间为 O(N)。假设您的值是合理/可预测的范围。 EX:适合 int 数据类型的东西。
  • 这里不需要任何排序。如果位图适合数据范围和计数,则使用位图要简单得多。
  • 另外,“如果 C++ 更高效……”。谢谢你的笑声。

标签: c sorting optimization duplicates


【解决方案1】:

这是哈希解决方案的快速伪代码,因此您可以了解其背后的“概念”。我会尝试将其设为 C,但不要假设它已经过编译和测试。但它会很接近。

#include <iostream>
using namespace std;

const int NUM_BITS = 32;

bool noDuplicates(const int INPUT[], const int SIZE, const int MIN_VALUE, const int MAX_VALUE) {

    const unsigned int RANGE = (MAX_VALUE - MIN_VALUE) / NUM_BITS;  //Use unsigned int, can support wider ranges this way.

    int isPresent[RANGE];// Might need dynamic allocation here, don't know if C supports this type of array initialization

    for(int i = 0; i < RANGE; i++) isPresent[i] = 0;//Probably don't need this loop on most systems.  Aslo, there are faster ways to zero memory.

    for(int i = 0; i < SIZE; i++) {

        const int ADJUST_TO_ZERO = INPUT[i] - MIN_VALUE; //adjust our min value to zero index now every possible value should map to an indice in our "isPresent" array
        const int INT_IN_ARRAY = ADJUST_TO_ZERO / NUM_BITS; // Each int represents 32 values, or our bit is hiding in the (VALUE/32)th slot
        const unsigned int BIT_VALUE = 1 << (ADJUST_TO_ZERO % NUM_BITS); // This is identical to 2 ^ (ADJUST_TO_ZERO % NUM_BITS)

        cout << "CHECKING: " << ADJUST_TO_ZERO << " ARRAY INDEX: " << INT_IN_ARRAY << " BIT:" << (ADJUST_TO_ZERO % NUM_BITS) << " INT REPRESENTATION: " << BIT_VALUE << endl;

        if(isPresent[INT_IN_ARRAY] & BIT_VALUE) { //bitwise &, with a value 2 ^ BIT, isolates this "BIT"
            return false;
        }

        isPresent[ADJUST_TO_ZERO / NUM_BITS] += BIT_VALUE; //If we add 2^BIT to an int, we are only adding the value to this to set this "BIT"
    }
    return true; //If we escape the loop above there are no duplicates
}


int main() {
    const int SIZE = 65;
    int array[SIZE];

    for(int i = 0; i < SIZE; i++) {
        array[i] = i;
    }

    array[SIZE - 1] = 30;

    cout << "RESULT: " << noDuplicates(array, SIZE, 0, 100) << endl;
}

【讨论】:

  • 这里唯一的问题是 isPresent[] 将是 4 GB,这肯定会导致堆栈分配失败,如此处所示,甚至可能在许多系统上使用 malloc() 失败。跨度>
  • MAX_VALUE 和 MIN_VALUE 是参数,而不是 unsigned int 的最大可能值。我还说这是概念的概述,当然,如果您的数组很大,您将需要动态分配,或者可能需要更智能(更具空间意识)的散列机制。
  • 他说他有十亿左右的价值,所以这意味着至少有十亿的范围。 C 没有紧凑的 bool 数组,因此该数组的每个值将是一个完整的 int(4 字节),即 4 GB。位图会好得多,即使这样对于堆栈分配来说也太大了。
  • 他拥有的值的数量绝对没有说明这些值可以获得的范围。
  • 如果他试图确保列表没有重复项,那么是的,它就是这样做的。
【解决方案2】:

你没有说你的值的范围是多少,但假设它是 32 位整数的范围,一个位图数组将是 512MB,这在大多数现代机器上都可以毫不费力地适应。试试这样的:

/* Assumes 32-bit ints */
int verify_unique( <data source> ) {
    unsigned int *bitmap = calloc(128 * 1024 * 1024, 4);
    if (!bitmap) { <error> }

    while ( <more input> ) {
        unsigned int value = <next value>;
        unsigned int index = value >> 5;
        unsigned int mask = 1 << (value & 0x1f);

        if (bitmap[index] & mask) {
            <found duplicate>
            break;
        }
        bitmap[index] |= mask;
    }
    free(bitmap);
}

【讨论】:

  • 嘿@Lee,我以前从未使用过位移。我试图通过谷歌搜索来理解这一点,但我做不到。你能提供一些 cmets 来推动我朝着正确的方向前进吗?谢谢。
  • 感谢您的链接。我一定会尝试了解您的解决方案。
【解决方案3】:

尝试counting sort 对数组进行排序,然后执行 link3 减去 link4 方法。应该足够有效。

【讨论】:

  • 使用计数排序时不需要执行“link3减link4”方法。即使对数组进行排序也没用,您只需要计数排序的count-array。此问题不需要算法的其他步骤。计数数组中的值将指示是否存在重复值。
  • 你知道,我读过那篇 wiki 文章,也有同样的想法,但我不确定。感谢您的确认。
猜你喜欢
  • 2021-08-16
  • 2019-08-23
  • 2017-12-06
  • 1970-01-01
  • 2011-06-02
  • 1970-01-01
  • 2018-06-15
  • 1970-01-01
相关资源
最近更新 更多