【问题标题】:Optimal method for creating a 2D Array without any repetitions创建没有任何重复的二维数组的最佳方法
【发布时间】:2014-05-26 07:00:03
【问题描述】:

我正在尝试创建一些代码来从大约 20 万到 100 万条记录的列表中找出记录。显然,我希望这个过程尽可能快。基本思想如下,大列表中的记录是要保持在一起的数字组合。例如:

0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,400076,400097,800076,800097
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200032,200078,500032,500078
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,300043,300083,600043,600083
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,600026,600077,900026,900077
0,0,0,0,0,0,0,0,0,0,0,0,0,0,100008,100028,400028,400056,600008,600056
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,400042,400098,500042,500098
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,86,500015,500086
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,400013,400076,800013,800076
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,700024,700083,900024,900083
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,100003,100047,800003,800047

记录的最大长度为 20,这就是附加零的原因。让我们暂时不要担心这些。因此,我想“捞出”一些记录,以免观察到重复。如果有重复,我可以丢弃该记录,不再进一步查看。因此,我必须编译一个如下所示的列表:

0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,400076,400097,800076,800097
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200032,200078,500032,500078
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,300043,300083,600043,600083
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,600026,600077,900026,900077
0,0,0,0,0,0,0,0,0,0,0,0,0,0,100008,100028,400028,400056,600008,600056
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,400042,400098,500042,500098
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,86,500015,500086
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,700024,700083,900024,900083
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,100003,100047,800003,800047

注意上面列表中的记录号。缺少 8,因为数字 400076 已存在于先前的记录中。

我用来执行此操作的代码如下:

void Make_List(ConfigList *pathgroups, ConfigList *configlist)
{
int i,j,k,l,flag,pg_num=0,len,p_num=0;
for(i = 0;i<configlist->num_total;i++)
{
    flag = 0;
    for(j = configlist->configsize-1;j>=0;j--)
    {   
        if(configlist->pathid[i][j])
        {
            for(k = 0;k<pg_num;k++)
            {
                for(l = pathgroups->configsize-1;l>=0;l--)
                {
                    if(pathgroups->pathid[k][l])
                    {
                        if(configlist->pathid[i][j]==pathgroups->pathid[k][l])
                        {
                            flag++;
                            break;
                        }
                    }
                    else
                    {
                        break;
                    }
                }
                if(flag)
                {
                    break;
                }
            }
        }
        else
        {
            break;
        }
        if(flag)
        {
            break;
        }
    }
    if(!flag)
    {
        len = 0;
        for(j = configlist->configsize-1;j>=0;j--)
        {
            pathgroups->pathid[pg_num][j]=configlist->pathid[i][j];
            if(configlist->pathid[i][j])
            {
                len++;
            }
        }
        pg_num++;
        p_num+=len;
        if(p_num>=totpaths)
        {
            break;
        }
    }   
}
Print_ConfigList(stderr,pathgroups);
}

ConfigList 结构基本上存储了二维数组以及程序不同部分中使用的其他内容。

num_total 告诉我们数组中的行数,而configsize 告诉我们数组中的列数。

totpaths 是一个断点,它会在分配完全完成时提前终止循环。

【问题讨论】:

  • @MBaas 抱歉,我应该提到。我正在使用 C
  • 我看到的最大数字是 900083。允许的最大数字是多少?
  • @user3386109 99999999 是允许的最大数字
  • 巴勃罗打败了我。创建一个 100000000 字节的数组。使用memset 将数组清0。在处理记录时,设置每个数字对应的字节。这样就很容易检查哪些号码已被使用。
  • @user3386109 我不明白你在说什么

标签: c arrays optimization bit-manipulation


【解决方案1】:

检查每个元素是否重复分析每个新元素的计算成本为O(N^2),考虑到您的输入集很大,这实在是太多了。

基本上,您需要的是一个快速访问的数据结构,您可以在其中记录您的记录出现的次数或至少一个布尔标志。

最简单的方法是有一个数组,其中位置表示每个可能的值,数组值表示位置值出现的次数(或其存在的布尔值)。但是,如果您的数据范围太大,您可以这样做,因为用于存储数组的内存与范围大小成正比。

避免这种情况的替代方法是使用哈希表或集合。

正如您在上面的 cmets 中建立的那样,您的整数范围是 [0,99999999],因此如果您想使用向量来跟踪每个单个值的存在与否,您需要大约 96 MB 将其存储在记忆。

这是一个使用字节数组的例子:

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

#define MAX_IN_RANGE 99999999

int main()
{

    char * isInInput = (char*)malloc(MAX_IN_RANGE+1);
    memset(isInInput,0,MAX_IN_RANGE+1);
    size_t i;
    int inputExample[] = {1,3,5,2,1,5};


    for(i = 0; i < 6; i++)
    {
        int value = inputExample[i];
        printf("%d\n",value);
        if(!isInInput[value])
        {
            printf("Add value %d to your collection\n", value);
            isInInput[value] = 1;
        }
        else
        {
            printf("%d is repeated\n", value);
        }
    }
    free(isInInput);
}

要改用哈希表,您可以依赖 Judy 等库来避免实现自己的哈希表。

【讨论】:

  • 数字不多,所以我很乐意这样做。大约有 900 个唯一数字,但范围从 1 到 900099
  • 我不想使用哈希表,因为它会使事情复杂化,因为我不知道该怎么做。我不介意数组
  • @AdityaSomani 如果您负担得起使用 95-96 MB 的内存,则可以避免使用 HashTables。问题在于,为了访问 O(1) 中的值计数,您需要将该值作为数组索引,因此您需要保留 100000000 个数组位置(范围的每个单个值一个)。是的,你我只有 900 个唯一数字,但你最初并不知道它们是范围内的哪些数字。
  • 非常重要:if (!isInInput[value]) isInInput[value] = 1;
  • 在 500K 记录上使用这种技术应该可以加快大约一百万倍的速度。这很好,我说:)
猜你喜欢
  • 2016-02-27
  • 1970-01-01
  • 1970-01-01
  • 2017-10-29
  • 2018-05-26
  • 1970-01-01
  • 2021-05-29
  • 1970-01-01
  • 2015-07-25
相关资源
最近更新 更多