2013-05-30 43 views
4

我想知道是否有一種甜美的方式,我可以在LINQ或類似的東西上做到這一點,但我試圖最均勻地分佈X部分的X字母字母整數> 0 & & < = 26.例如,這裏可能有一些可能的輸出。大多數均勻地分佈在字母順序上的字母

  • X = 1:1個分區26
  • X = 2:13
  • 2個分區X = 3:9 2個 分區和8
  • 等一個分區....

最終,我不想有任何分區,最終沒有得到至少一個,我的目標是讓他們實現最均勻的分佈,分區大小之間的差異範圍是一樣小盡可能地。

這是我嘗試orginally代碼:

char[] alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray(); 
int pieces = (int)Math.Round((double)alphabet.Count()/numberOfParts); 
for (int i = 0; i < numberOfParts.Count; ++i) { 
    char[] subset = i == numberOfParts.Count - 1 ? alphabet.Skip(i * pieces).ToArray() 
        : alphabet.Skip(i * pieces).Take(pieces).ToArray(); 
... // more code following 

這似乎首先是工作的罰款,但我在測試中意識到,有一個問題,當X基於這種邏輯,我得到的是10。 8組3和1組2,離開第10組0,這是不好的,因爲我正在進行最均勻的分配。

在這種情況下,10的最理想分佈將是6組3和4組2.如何實現這個想法?

+0

你的問題聽起來很具體和不明確,你能詳細說明你想在這裏實現什麼?這課程也是?如果是這樣,這不是一個問題,但是可以告訴我們,因爲這可能會影響您得到的答案! –

+0

不,這是不是作業,我試圖根據開始的信件分發消息到不同的位置。 –

+0

夠公平 - 你能詳細說明原因嗎?我有一種感覺,這裏有一個聰明的答案和一個很好的答案! –

回答

3

一般來說,實現邏輯的最簡單方法是使用模運算符%。熟悉這個操作符;它對於有幫助的情況非常有用。有許多方法來編寫實際的代碼做的字母分佈,使用數組或沒有如你所願等等,但是這短短的表達應該給你一個想法:

"ABCDEFGHIJKLMNOPQRSTUVWXYZ".IndexOf(letter) % partitionCount 

這個表達式給出了零在其中存放大寫字母的分區的索引。這個字符串只是爲了方便而顯示的,但可以是數組或表示字母表的其他方式。您可以循環使用字母表,使用與上述類似的邏輯來選擇每個字母的存放位置。至於你會在哪裏把邏輯:在一個循環內,在一個方法等。

模塊化算術沒有什麼神奇的;在達到可用數字集合的末尾之後,它只是「環繞」。一個我們都遇到過的簡單背景是在劃分中; %運算符實際上只是給出了餘數。現在你明白了%操作符在做什麼,你可以很容易地編寫自己的代碼來用任何語言來做同樣的事情。

把所有這些組合起來,你可以寫一個程序,類或擴展方法像這樣的 - 請注意%來計算剩餘,而簡單的整數除法丟棄:

/// <summary> 
/// Returns partition sized which are as close as possible to equal while using the indicated total size available, with any extra distributed to the front 
/// </summary> 
/// <param name="totalSize">The total number of elements to partition</param> 
/// <param name="partitionCount">The number of partitions to size</param> 
/// <param name="remainderAtFront">If true, any remainder will be distributed linearly starting at the beginning; if false, backwards from the end</param> 
/// <returns>An int[] containing the partition sizes</returns> 
public static int[] GetEqualizedPartitionSizes(int totalSize, int partitionCount, bool remainderAtFront = true) 
{ 
    if (totalSize < 1) 
     throw new ArgumentException("Cannot partition a non-positive number (" + totalSize + ")"); 
    else if (partitionCount < 1) 
     throw new ArgumentException("Invalid partition count (" + partitionCount + ")"); 
    else if (totalSize < partitionCount) 
     throw new ArgumentException("Cannot partition " + totalSize + " elements into " + partitionCount + " partitions"); 

    int[] partitionSizes = new int[partitionCount]; 
    int basePartitionSize = totalSize/partitionCount; 
    int remainder = totalSize % partitionCount; 
    int remainderPartitionSize = basePartitionSize + 1; 
    int x; 

    if (remainderAtFront) 
    { 
     for (x = 0; x < remainder; x++) 
      partitionSizes[x] = remainderPartitionSize; 

     for (x = remainder; x < partitionCount; x++) 
      partitionSizes[x] = basePartitionSize; 
    } 
    else 
    { 
     for (x = 0; x < partitionCount - remainder; x++) 
      partitionSizes[x] = basePartitionSize; 

     for (x = partitionCount - remainder; x < partitionCount; x++) 
      partitionSizes[x] = remainderPartitionSize; 
    } 

    return partitionSizes; 
} 
+0

請注意,現在,這個答案把'A'和'B'在一組中有多個組。原始問題和其他答案試圖分割連續索引,如分頁。 –

+0

@馬克赫德:謝謝。我已經更新了答案。 – Iucounu

1

我做了什麼在一個應用程序,我不得不在小組分配的東西是這樣的

var numberOfPartitions = GetNumberOfPartitions(); 
var numberOfElements = GetNumberOfElements(); 

while (alphabet.Any()) 
{ 
    var elementsInCurrentPartition = Math.Ceil((double)numberOfPartitions/numberOfElements) 

    for (int i = 0; i < elementsInCurrentPartition; i++) 
    { 
      //fill your partition one element at a time and remove the element from the alphabet 
      numberOfElements--; 
    } 
    numberOfPartitions--; 
} 

這不會給你所期望的確切結果(10個分區,即理想的結果),但它非常接近。

p.s.這不是測試:)

1

僞碼算法,現在我已經測試:

Double count = alphabet.Count() 
Double exact = count/numberOfParts 
Double last = 0.0 
Do Until last >= count 
    Double next = last + exact 
    ranges.Add alphabet, from:=Round(last), to:=Round(next) 
    last = next 
Loop 

ranges.Add可以忽略空的範圍:-)

Here是LinqPad VB.NET實現這個算法。

所以這個LINQ的版本會是這樣的

Double count = alphabet.Count(); 
Double exact = count/numberOfParts; 

var partitions = Enumerable.Range(0, numberOfParts + 1).Select(n => Round((Double)n * exact)); 

Here是使用這個Linq查詢一個LinqPad VB.NET實現。

2

我覺得最簡單的方法是對每個字母執行循環分配。循環瀏覽字母表中的每個字母並添加到字母表中,然後重複。有一個運行計數,確定你將把物品放入什麼字母,然後當它> 26時,將它重置爲0!

+0

增加了極端概念的簡單性。像這樣的例行公事通常不會成爲一個問題,對於一般的KISS方法還有很多要說的。 – Iucounu

0

(抱歉格式,移動)

首先,你需要像分批處理法:

public static IEnumerable<IEnumerable<T>> Batch<T>(this IEnumerable<T> source, int groupSize) 
{ 
    var tempSource = source.Select(n => n); 
    while (tempSource.Any()) 
    { 
     yield return tempSource.Take(groupSize); 
     tempSource = tempSource.Skip(groupSize); 
    } 
} 

然後,就這樣稱呼它:

var result = alphabet.Batch((int)Math.Ceiling(x/26.0));