【问题标题】:Heavy computations analysis/optimization大量计算分析/优化
【发布时间】:2011-03-18 01:55:33
【问题描述】:

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

  1. 给定的 0 和 1 序列被分成块。假设序列的长度为 S,块的长度为 N,即为 2 的幂(4、8、16、32 等)。块的数量是 n=S/N,这里没有火箭科学。
  2. 根据选择的 N 我正在构建一组所有可能的 N 位二进制数,这是 2^N-1 个对象的集合。
  3. 在此之后,我需要将每个二进制数与源序列中的每个块进行比较,并计算每个二进制数匹配的次数,例如:
    S : 000000001111111100000000111111110000000011111111...(0000000011111111 重复 6 次,16 位 x 6 = 96 位)
    N : 8
    块:{00000000, 11111111, 00000000, 1111111,...}
    计算:

.

// _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 很小,我似乎也有 (2^N-1)(S/N) 次迭代,当 N=64 时,这个数字会增长到 2^64=(long 类型的最大值),所以并不漂亮.我确信有必要优化循环并且可能从根本上改变方法(N=32 的 c# 实现需要 2h @ dual-core pc w/Parallel.For)。有什么想法可以减少上述方案的时间和资源消耗?似乎我必须预先计算二进制数并通过从文件中读取“i”来摆脱第一个循环,并用块“即时”评估它,但文件大小将是 (2^N)*N 字节(( 2^N-1)+1)*N) 这在某种程度上也是不可接受的。

【问题讨论】:

  • 请对您的问题应用一些格式。它不清晰。
  • 等等——你正在使用 C# 但没有乘法或除法?
  • 您是否针对 Convert.ToInt64(string number, int fromBase) (msdn.microsoft.com/en-us/library/yk8t68tb.aspx) 分析了 ParseBinaryNumber 函数?
  • @Lucas Heneks 感谢您对标记的帮助。 @Billy ONeal 我在这里没有它们,就像在一些重量级的递归公式中一样。只需对大量对象进行一次简单的评估。
  • 我投票结束,因为我在任何地方都看不到任何问题。我们希望能为您提供什么帮助?

标签: c# c++ algorithm optimization statistics


【解决方案1】:

您似乎想要的是计算每个特定块在您的序列中出现的次数;如果是这种情况,将每个块与所有可能的块进行比较,然后进行统计是一种可怕的方法。你最好制作一个将块映射到计数的字典;像这样:

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。

【讨论】:

  • 看来这种方法是可以接受的。我也在计算源中不存在的块,但你说得对,更容易枚举可用块,然后从 2^N 中减去它们的数量,这将做同样的事情。谢谢。
【解决方案2】:

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

这不是memcmp 的用途吗?

您正在循环遍历所有可能的整数值,这需要 2 个小时,您对此感到惊讶吗?如果您需要进行如此多的迭代,那么您可以做的事情不多。

【讨论】:

    【解决方案3】:

    您是否尝试获取 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”使用一个副本,但这很容易。至于并行化,字典是最简单的,因为您可以将字符串分成与线程一样多的部分,为每个部分做一个字典,然后组合字典本身以获得最终计数。第二,我想排序本身可以很容易地并行化,然后有一个最后的通过来获得计数。第三个将要求您对每个子字符串进行标记化,然后在最终重组的字符串上重做。

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

    【讨论】:

    • 嗯,听起来很合理。上面的内容是对一些涉及克罗内克 delta 函数的随机过程公式的直接解释,该函数可以很容易地从 f(i,j) 恢复为 f(j,i)。唯一的问题是对于那个特定情况是否可能......我不想破坏系统的其他部分。无论如何,谢谢。
    【解决方案4】:

    除了字典,您还可以使用平面文件,大小为 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
    

    字典一开始在空间上是保守的,以后需要查找正确的索引。如果您最终期望所有可能的整数,则可以从一开始就保留一个固定大小的“记分卡”。

    【讨论】:

    • 如果 N 相对较小,例如在您的示例中为 8,则像这样使用整数进行计数的记分卡将允许您为每个记分卡使用 2^8*4 == 1024 字节的内存。当您跟踪更大的位宽值时,这会迅速增长。
    猜你喜欢
    • 2015-09-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-29
    • 2012-07-26
    • 1970-01-01
    • 2018-05-18
    相关资源
    最近更新 更多