我刚刚完成了计算机科学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;
}
答案 0 :(得分:16)
它或多或少存在,但并不完全。在大多数情况下,它是O(n),但如果每个ticketNum都大于每个winsNum,则为O(n ^ 2)。 (这是因为内部j
循环不应该break
j==6
,而是运行下一个i
迭代。)
您希望算法在每一步增加i
或j
,并在i==6
或j==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);在每个阶段,它可以递增i
或j
,因此它可以执行的最多步骤是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的表现。