繁重的计算分析/优化

时间:2010-07-19 19:00:03

标签: c# c++ algorithm optimization statistics

首先,我没有乘法,除法运算,因此我可以使用移位/加法,溢出乘法,预先计算等。我只是将一个n位二进制数与另一个进行比较,但根据算法,这种行动的数量似乎很大。这是:

  1. 给出了0和1的序列,它被分成块。假设序列的长度为S,则块的长度为N,其为2的幂(4,8,16,32等)。块数量是n = S / N,这里没有火箭科学。
  2. 根据所选择的N i构建一组所有可能的N位二进制数,它是2 ^ N-1个对象的集合。
  3. 在此之后,我需要将每个二进制数与来自源序列的每个块进行比较,并计算每个二进制数匹配的次数,例如:
    S:000000001111111100000000111111110000000011111111 ...(0000000011111111重复6次,16位x 6 =总共96位)
    N:8
    块:{00000000,111111111,00000000,111111,...}
    计算:
  4. // _n = S/N;
    // _N2 = Math.Pow(2,N)-1
    // S=96, N=8, n=12, 2^N-1=255 for this specific case
    // sourceEpsilons = list of blocks from input, List<string>[_n]
    var X = new int[_n]; // result array of frequencies
    for (var i = 0; i < X.Length; i++) X[i] = 0; // setting up
    for (ulong l = 0; l <= _N2; l++) // loop from 0 to max N-bit binary number
    var currentl = l.ToBinaryNumberString(_N/8); // converting counter to string, getting "current binary number as string"
    var sum = 0; // quantity of currentl numbers in blocks array
    for (long i = 0; i < sourceEpsilons.LongLength; i++)
    {
       if (currentl == sourceEpsilons[i]) sum++; // evaluations of strings, evaluation of numbers (longs) takes the same time
    }
    // sum is different each time, != blocks quantity                    
    for (var j = 0; j < X.Length; j++) 
    if (sum - 1 == j) X[j]++; // further processing
    // result : 00000000 was matched 6 times, 11111111 6 times, X[6]=2. Don't ask me why do i need this >_<
    

    即使很小的S i似乎也有(2 ^ N-1)(S / N)次迭代,N = 64,数字增长到2 ^ 64 =(long类型的最大值)所以这不是很漂亮。我确信需要优化循环并且可能更改基本方法(N = 32的c#实现需要2h @双核pc / Parallel.For)。任何想法如何使上述方案减少时间和资源消耗?看起来我必须预先计算二进制数并通过从文件中读取“i”来摆脱第一个循环并使用块“即时”评估它,但文件大小将是(2 ^ N)* N个字节(( 2 ^ N-1)+1)* N)这也是不可接受的。

4 个答案:

答案 0 :(得分:6)

看起来你想要的是计算序列中每个特定块发生的次数;如果是这种情况,将每个块与所有可能的块进行比较,然后统计是一种可怕的方式。制作一个将块映射到计数的字典要好得多;像这样的东西:

var dict = Dictionary<int, int>();
for (int j=0; j<blocks_count; j++)
{
    int count;
    if (dict.TryGetValue(block[j], out count)) // block seen before, so increment
    {
        dict[block[j]] = count + 1;
    }
    else // first time seeing this block, so set count to 1
    {
        dict[block[j]] = 1; 
    }
}

在此之后,任何特定块的计数q将在dict[the_block]中,如果该键不存在,则计数为0.

答案 1 :(得分:0)

  

我只是将一个n位二进制数与另一个

进行比较

这不是memcmp的用途吗?

你正在循环遍历每个可能的整数值,这需要2个小时,而你却感到惊讶?如果你需要迭代那么多,你可以做很多精简事情。

答案 2 :(得分:0)

您是否尝试在S中获取唯一消息的数量?例如,在您的示例中,对于N = 2,您会收到2条消息(0011),N = 4您会收到2条消息,(00001111),N = 8您收到1条消息(00001111)。如果是这种情况,那么tzaman建议的字典方法就是一种方法。另一个是首先对列表进行排序,然后运行它并查找每条消息。第三种天真的方法是使用标记消息,例如全部为0,并通过寻找不是标记的消息来运行。当你找到一个时,通过将它们设置为哨兵来销毁它的所有副本。例如:

int CountMessages(char[] S, int SLen, int N) {
    int rslt = 0;
    int i, j;
    char *sentinel;

    sentinel = calloc((N+1)*sizeof(char));

    for (i = 0; i < N; i ++)
        sentinel[i] = '0';

    //first, is there a sentinel message?
    for (i = 0; ((i < SLen) && (rslt == 0)); i += N) {
        if (strncmp(S[i], sentinel, N) == 0)
            rslt++;
    }

    //now destroy the list and get only the unique messages
    for (i = 0; i < SLen; i += N) {
        if (strncmp(S[i], sentinel, N) != 0) { //first instance of given message
            rslt++;                
            for (j = i+N; j < SLen; j += N) { //look for all remaining instances of this message and destroy them
                if (strncmp(S[i], S[j], N) == 0)
                    strncpy(S[j], sentinel, N); //destroy message
            }
        }
    }

    return rslt;
}

第一种方法是使用预先编写的字典或编写自己的字典。第二个和第三个会破坏列表,这意味着您必须为要测试的每个“N”使用副本,但这很容易。至于并行化,字典是最简单的,因为您可以将字符串分解为与线程一样多的部分,为每个部分执行字典,然后组合字典本身以获取最终计数。对于第二种,我认为排序本身可以很容易地并行,然后有一个最后的传递来计算。第三个要求你对每个子串进行哨兵化,然后在最后重组的字符串上重做它。

请注意这里的重要想法:而不是循环遍历所有可能的答案,你只能遍历所有数据!

答案 3 :(得分:0)

您也可以使用大小为2 ^ N个条目的平面文件,而不是字典,每个条目的大小都是整数。

这将是你的计数垫。而不是循环遍历集合中的所有可能数字,并与您当前查看的数字进行比较,您只能这样迭代S向前:

procedure INITIALIZEFLATFILE is
    allocate 2^N * sizeof(integer) bytes to FLATFILE
end procedure

procedure COUNT is
    while STREAM is not at END
        from FLATFILE at address STREAM.CURRENTVALUE read integer into COUNT
        with FLATFILE at address STREAM.CURRENTVALUE write integer COUNT+1
        increment STREAM
    end while
end procedure

字典在开头的空格上是保守的,并且需要稍后查找正确的索引。如果你最终期望所有可能的整数,你可以从getgo中保留一个固定大小的“记分卡”。