放置数组元素的最快方式是在限制之间出现的次数

时间:2011-10-25 13:51:20

标签: c# arrays performance algorithm processing-efficiency

例如......

  1. 我有一个整数数组,它由1到1000之间的随机值初始化
  2. 数组有1M个元素(它可能有更多,但这只是一个例子)
  3. 每个元素的出现次数必须介于10到1010之间
  4. 调整此Array元素的最快方法是什么,以便它们符合上述标准?

    如果最大出现次数接近于array,则我的第一个解决方案太慢了.Length(1M)/ valuesSpan(1000)

    我尝试了类似的东西(这只是为了对齐最大出现次数,下限的解决方案几乎相同):

    Int64[] DistinctArrayElements = distinctArrayElements;
    Dictionary<Int64, Int32> occurrences = new Dictionary<Int64, Int32>();
    
    foreach (Int64 DistinctElement in DistinctArrayElements)
    {
        occurrences.Add(DistinctElement, 0);
    }
    
    foreach (Int64 ArrayElement in Arr)
    {
        occurrences[ArrayElement] += 1;
    }
    //I know this initialization can be done more nicely, so don't bother with this.
    
    for (int j = 0; j < Arr.Length; j++)
    {
        if (occurrences[Arr[j]] > upperNoOfOccurrences)
        {
            for (int i = 0; i < Arr.Length; i++)        
            {
                if (occurrences[Arr[i]] < upperNoOfOccurrences)
                {
                    Arr[j] = Arr[i];
                    occurrences[Arr[i]] += 1;
                    occurrences[Arr[j]] -= 1;
                }
            }
        }
    }
    

3 个答案:

答案 0 :(得分:0)

我无法从你想做的事情中找到真正的意义。但是,多次处理阵列似乎是一种浪费。你可以只用一个循环(当你用完“免费”唯一值时稍微向前偷看)。以下当然不是我编写的最好的代码,但我认为它可以解决您的问题。

HashSet<long> forbidden = new HashSet<long>(); // maximum size of 1000, contains values that exceeded the limit
Queue<long> remaining = new Queue<long>(1000); // stores found unique values within the limit in a queue, that will be used if we bounce into the limit
Dictionary<long, int> frequencies = new Dictionary<long, int>(1000);
int lastPeekIndex = 0;
for (int i = 0; i < Arr.Length; i++) {
  if (!frequencies.ContainsKey(Arr[i])) {
    frequencies[Arr[i]] = 0;
    remaining.Add(Arr[i]);
  }

  if (frequencies[Arr[i]] == upperLimit) {
    if (!forbidden.Contains(Arr[i])) forbidden.Add(Arr[i]);
    var next = Int64.MinValue;
    try {
      next = remaining.Dequeue();
      while (forbidden.Contains(next)) next = remaining.Dequeue();
    } catch (InvalidOperationException) { // Arrr! we have not yet observed enough unique values
      for (int j = Math.Max(i, lastPeekIndex) + 1; j < Arr.Length; j++)
        if (!frequencies.ContainsKey(Arr[j])) {
          frequencies[Arr[j]] = 0;
          next = Arr[j];
          lastPeekIndex = j;
        }
    }
    Arr[i] = next;
    frequencies[next]++;
    if (frequencies[next] < upperLimit) remaining.Enqueue(next);
  } else frequencies[Arr[i]]++;
}

请注意,这不会检查下限,因为您也没有检查。我认为你必须关心在第二次传球中不经常出现的价值。您可以在第一次传递后将它们放在另一个队列中,然后一次又一次地遍历数组,直到队列为空(在第二次传递中可能甚至少于一次完整的迭代)。

答案 1 :(得分:0)

我会对你的字典进行排序,以便首先显示较少的数字。这样,您不必每次都查找合适的数字,只需将其替换为具有较少出现次数的数字。这是一个伪代码来解释:

struct dict {
    key, value
}

linkedList<dict> occurrences;

initialize occurrences
sort it (smallest values first)

// start from the one with greatest number of occurrences
n = occurrences.last;

// keep going until occurrences of n is greater than upperNoOfOccurrences
while n.value.value > upperNoOfOccurrences and didn't reach first element
    repeat = true

    do:
        // required occurrences to subtract to be within the limit
        required = upperNoOfOccurrences - n.value.value

        // maximum occurrences we can add to the first
        maxAllowed = upperNoOfOccurrences - occurrences.first.value.value

        // if we can do that
        if required < maxAllowed:
            occurrences.first.value.value += required
            n.value.value -= required
            repeat = false
        else:    // n.value.value is still greater than upperNoOfOccurrences
            occurrences.first.value.value += maxAllowed 
            n.value.value -= maxAllowed 
            repeat = true
        end if

        // keep occurrences sorted
        newPos = occurrences.first.next
        while occurrences.first.value > newPos.value.value:
            newPos = newPos.next

        move occurrences.first before newPos
    while repeat
end while

now rebuild your array with occurrences. it will
be sorted  but it doesn't matter does it? ;)

答案 2 :(得分:0)

这是一个简单而精确的方法,可以统一采样符合您标准的数字集。

  1. 设M =不同值的数量; N =数组元素的数量; L =每个值的实例计数的下限; U =计数上限; D = U-L。例如,M = 1000,N = 1000000,L = 10,U = 1010,D = 1000。
  2. 创建大小为M * D的数组S.将S的前N个条目设置为1,其余条目设置为零。
  3. Shuffle S通过Fisher-Yates shuffle。 (参见链接here
  4. 创建大小为M的数组T.
  5. 对于i直到M,设置T [i] = L + S [i D] + S [i D + 1] + ... + S [i * d + d-1]。
  6. 创建大小为N的数组V.用第0个值的T [0]实例填充它,依此类推, ie 使用i的T [i]个实例每个i的价值。因为S包含N 1,所以V将完全准确地填充。
  7. Shuffle V通过Fisher-Yates shuffle。然后数组V满足原始标准。
  8. 注意步骤2-5是O(M D),而6-7是O(N + M),后者和可以实现的一样好,前者可能同样如此,因为M < / em> D在您的问题陈述中是O(N)。