线性搜索算法优化

时间:2010-08-29 17:04:46

标签: c performance algorithm

我刚刚完成了计算机科学1的作业问题(是的,这是作业,但是听我说!)。现在,作业完成并且有效,所以我不需要帮助。我的问题涉及我正在使用的算法的效率(我们没有对算法效率进行评分,我只是非常好奇)。

我即将呈现的功能目前使用线性搜索算法的修改版本(我想出的,全部由我自己!),以便检查给定彩票上有多少数字与中奖号码匹配,假设票证上的数字和绘制的数字都是按升序排列的。我想知道,有没有办法让这个算法更有效率?

/*
 * Function: ticketCheck
 *
 * @param struct ticket
 * @param array winningNums[6]
 *
 * Takes in a ticket, counts how many numbers
 * in the ticket match, and returns the number
 * of matches.
 *
 * Uses a modified linear search algorithm,
 * in which the index of the successor to the
 * last matched number is used as the index of
 * the first number tested for the next ticket value.
 *
 * @return int numMatches
 */
int ticketCheck( struct ticket ticket, int winningNums[6] )
{
    int numMatches = 0;
    int offset = 0;
    int i;
    int j;

    for( i = 0; i < 6; i++ )
    {
        for( j = 0 + offset; j < 6; j++ )
        {
            if( ticket.ticketNum[i] == winningNums[j] )
            {
                numMatches++;
                offset = j + 1;
                break;
            }
            if( ticket.ticketNum[i] < winningNums[j] )
            {
                i++;
                j--;
                continue;
            }
        }
    }

    return numMatches;
}

5 个答案:

答案 0 :(得分:16)

它或多或少存在,但并不完全。在大多数情况下,它是O(n),但如果每个ticketNum都大于每个winsNum,则为O(n ^ 2)。 (这是因为内部j循环不应该break j==6,而是运行下一个i迭代。)

您希望算法在每一步增加ij,并在i==6j==6时终止。 [您的算法几乎满足此要求,如上所述。]因此,您只需要一个循环:

for (i=0,j=0; i<6 && j<6; /* no increment step here */) {
    if (ticketNum[i] == winningNum[j]) {
        numMatches++;
        i++;
        j++;
    }
    else if (ticketNum[i] < winningNum[j]) {
        /* ticketNum[i] won't match any winningNum, discard it */
        i++;
    }
    else { /* ticketNum[i] > winningNum[j] */
        /* discard winningNum[j] similarly */
        j++;
    }
}

显然这是O(n);在每个阶段,它可以递增ij,因此它可以执行的最多步骤是2 * n-1。这与您的算法具有几乎相同的行为,但更容易理解,更容易看出它是正确的。

答案 1 :(得分:7)

你基本上是在寻找两组相交的大小。鉴于大多数lottos使用大约50个球(左右),您可以将数字存储为以无符号长整数设置的位。找到共同的数字就是将两者加在一起的简单问题:commonNums = TicketNums & winningNums;

查找交集的大小是计算结果数中的一位,即covered previously的主题(尽管在这种情况下,您使用的是64位数字,或者一对32位数字,而不是一个32位数字。)

答案 2 :(得分:2)

是的,有更快的东西,但可能使用更多的内存。使用可能数字大小的数组填充0,在每个绘制的数字上加1。对于每个票号,在该号码的索引处添加值。

 int NumsArray[MAX_NUMBER+1];
 memset(NumsArray, 0, sizeof NumsArray);

 for( i = 0; i < 6; i++ )
   NumsArray[winningNums[i]] = 1;

 for( i = 0; i < 6; i++ )
   numMatches += NumsArray[ticket.ticketNum[i]];

12循环而不是最多36循环 周围的代码留作练习。

编辑:它还具有不需要对两组值进行排序的优点。

答案 3 :(得分:0)

这实际上只是这样的尺度上的微小变化,但如果第二个循环达到的数字大于当前的票号,则它已被允许制动。此外,如果您的秒数遍历的数字低于您的票号,即使在该次迭代中未找到匹配项,它也可能会更新偏移量。

PS: 不要忘记,如果我们将球的数量或票的大小变化,那么效率的一般结果会更有意义。否则它太依赖于机器。

答案 4 :(得分:0)

如果不是比较彩票数字的数组,而是创建两位数字的标志 - 如果它的索引位于该数组中,则每个标志都被设置 - 那么你可以按位执行两位数组(彩票和中奖号码设置)并产生另一个位数组,其位是仅用于匹配数字的标志。然后计算设置的位数。

对于许多彩票来说64位就足够了,所以uint64_t应该足够大以涵盖这一点。此外,一些体系结构具有计数寄存器中设置的位的指令,一些编译器可能能够识别和优化这些位。

该算法的效率基于彩票号码(M)的范围和每张彩票的彩票号码数量(N)。设置标志是否为O(N),而两位数组的排序和位数的计数可以是O(M),这取决于你的M(乐透号码范围)是否大于target cpu可以直接执行这些操作。但最有可能的是,M会很小,其影响可能会小于N的表现。