Related
I tried BlockingCollection and ConcurrentDictionary. It continues by adding BlockingCollection items as soon as the thread is completed, but ConcurrentDictionary is always adding thread order. Is the ConcurrentDictionary waiting for the previous action to complete to add an item? Or does it order after adding? If ConcurrentDictionary adds item by thread order, is there any point in using parallel? Why does ConcurrentDictionary work this way while BlockingCollection works properly?
Codes and output is below:
BlockingCollection<string> blockingCollection = new BlockingCollection<string>();
ConcurrentDictionary<int, string> concurrentDictionary =
new ConcurrentDictionary<int, string>();
var rand = new Random();
Parallel.For(0, 10, i =>
{
int wait = rand.Next(10, 500);
Thread.Sleep(wait);
blockingCollection.Add($"t:{i} waittime:{wait}");
});
Parallel.For(0, 10, i =>
{
int wait = rand.Next(10, 500);
Thread.Sleep(wait);
concurrentDictionary.TryAdd(i, $"t:{i} waittime:{wait}");
});
Console.WriteLine("--BlockingCollection--");
Console.WriteLine(string.Join("\n", blockingCollection.ToArray()));
Console.WriteLine("--ConcurrentDictionary--");
Console.WriteLine(string.Join("\n", concurrentDictionary
.Select(x => $"Key:{x.Key} Value:{x.Value}").ToArray()));
Output:
The ConcurrentDictionary<TKey,TValue> has no notion of order. When you enumerate it, you can't make any assumption regarding the order in which the keys will be emitted. You are not even allowed to assume that a single enumeration will yield unique keys. You could study the source code of the class and try to get a better understanding of how the keys are stored, but it will be a daunting task. Even worse, the value of this knowledge will evaporate as soon as the next version of the .NET runtime is released. Microsoft reserves the right to change the behavior of any API, as long as the affected behavior is not documented. And making the correctness of your program depend on implementation details of code that you don't control, is not something that most developers are comfortable with, and for good reasons.
Just for fun I wrote the demo below, that shows how the collection behaves for shuffled sequences that contains elements incremented by different steps:
for (int step = 1; step <= 10; step++)
{
Random random = new(0);
Console.WriteLine($"Step: {step}");
int[] source = Enumerable.Range(1, 15)
.OrderBy(_ => random.Next())
.Select(x => x * step)
.ToArray();
Console.WriteLine($"Source shuffled: [{String.Join(", ", source)}]");
Console.WriteLine($"Source ordered: [{String.Join(", ", source.Order())}]");
ConcurrentDictionary<int, string> dictionary = new();
foreach (int i in source) dictionary.TryAdd(i, null);
Console.WriteLine($"dictionary.Keys: [{String.Join(", ", dictionary.Keys)}]");
Console.WriteLine();
}
Output:
Step: 1
Source shuffled: [5, 10, 11, 8, 12, 14, 4, 6, 13, 1, 3, 2, 7, 9, 15]
Source ordered: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
dictionary.Keys: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
Step: 2
Source shuffled: [10, 20, 22, 16, 24, 28, 8, 12, 26, 2, 6, 4, 14, 18, 30]
Source ordered: [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30]
dictionary.Keys: [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30]
Step: 3
Source shuffled: [15, 30, 33, 24, 36, 42, 12, 18, 39, 3, 9, 6, 21, 27, 45]
Source ordered: [3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45]
dictionary.Keys: [33, 3, 36, 6, 39, 9, 42, 12, 45, 15, 18, 21, 24, 27, 30]
Step: 4
Source shuffled: [20, 40, 44, 32, 48, 56, 16, 24, 52, 4, 12, 8, 28, 36, 60]
Source ordered: [4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60]
dictionary.Keys: [4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60]
Step: 5
Source shuffled: [25, 50, 55, 40, 60, 70, 20, 30, 65, 5, 15, 10, 35, 45, 75]
Source ordered: [5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75]
dictionary.Keys: [65, 35, 5, 70, 40, 10, 75, 45, 15, 50, 20, 55, 25, 60, 30]
Step: 6
Source shuffled: [30, 60, 66, 48, 72, 84, 24, 36, 78, 6, 18, 12, 42, 54, 90]
Source ordered: [6, 12, 18, 24, 30, 36, 42, 48, 54, 60, 66, 72, 78, 84, 90]
dictionary.Keys: [66, 36, 6, 72, 42, 12, 78, 48, 18, 84, 54, 24, 90, 60, 30]
Step: 7
Source shuffled: [35, 70, 77, 56, 84, 98, 28, 42, 91, 7, 21, 14, 49, 63, 105]
Source ordered: [7, 14, 21, 28, 35, 42, 49, 56, 63, 70, 77, 84, 91, 98, 105]
dictionary.Keys: [63, 35, 98, 7, 70, 42, 105, 14, 77, 49, 21, 84, 56, 28, 91]
Step: 8
Source shuffled: [40, 80, 88, 64, 96, 112, 32, 48, 104, 8, 24, 16, 56, 72, 120]
Source ordered: [8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 88, 96, 104, 112, 120]
dictionary.Keys: [32, 64, 96, 8, 40, 72, 104, 16, 48, 80, 112, 24, 56, 88, 120]
Step: 9
Source shuffled: [45, 90, 99, 72, 108, 126, 36, 54, 117, 9, 27, 18, 63, 81, 135]
Source ordered: [9, 18, 27, 36, 45, 54, 63, 72, 81, 90, 99, 108, 117, 126, 135]
dictionary.Keys: [63, 126, 36, 99, 9, 72, 135, 45, 108, 18, 81, 54, 117, 27, 90]
Step: 10
Source shuffled: [50, 100, 110, 80, 120, 140, 40, 60, 130, 10, 30, 20, 70, 90, 150]
Source ordered: [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150]
dictionary.Keys: [130, 100, 70, 40, 10, 140, 110, 80, 50, 20, 150, 120, 90, 60, 30]
Online demo.
So apparently the ConcurrentDictionary<TKey,TValue> emits the keys in a sorted order when the step is 1, 2 or 4, otherwise there is no obvious order. Which is a useless knowledge, but there you have it!
I'm creating some remote control tool in C# from Windows to Linux which I got working.
The only problem I'm facing is translating the key codes from Windows to Linux. Is there any formula, table or tool to do this conversion?
If it helps, on windows I get the key codes from the KeyUp/KeyDown events and on the linux side i'm using uinput to generate the events.
Cheers.
Ok, I found an elegant solution based on an idea of Dietrich Epp. The idea is based on using the USB HID codes as an intermediate common point.
I got from here the linux key mappings to HID and from here the windows ones.
And this is the final result:
public static byte[] linux_hid_keyboard = new byte[]
{
255, 255, 255, 255, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38,
50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44, 2, 3,
4, 5, 6, 7, 8, 9, 10, 11, 28, 1, 14, 15, 57, 12, 13, 26,
27, 43, 43, 39, 40, 41, 51, 52, 53, 58, 59, 60, 61, 62, 63, 64,
65, 66, 67, 68, 87, 88, 99, 70,119,110,102,104,111,107,109,106,
105,108,103, 69, 98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71,
72, 73, 82, 83, 86,127,116,117,183,184,185,186,187,188,189,190,
191,192,193,194,134,138,130,132,128,129,131,137,133,135,136,113,
115,114,255,255,255,121,255, 89, 93,124, 92, 94, 95,255,255,255,
122,123, 90, 91, 85,255,255,255,255,255,255,255,111,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,179,180,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,111,255,255,255,255,255,255,255,
29, 42, 56,125, 97, 54,100,126,164,166,165,163,161,115,114,113,
150,158,159,128,136,177,178,176,142,152,173,140,255,255,255,255
};
public static byte[] windows_hid_keyboard = new byte[]
{
255,255,255,255, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76,
77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 49, 50,
51, 52, 53, 54, 55, 56, 57, 48, 13, 27, 8, 9, 32,189,187,219,
221,220,255,186,222,192,188,190,191, 20,112,113,114,115,116,117,
118,119,120,121,122,123, 44,145, 19, 45, 36, 33, 46, 35, 34, 39,
37, 40, 38,144,111,106,109,107,255, 97, 98, 99,100,101,102,103,
104,105, 96,110,255,255,255,255,124,125,126,127,128,129,130,131,
132,133,134,135,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
17, 16, 18,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255
};
public static Dictionary<int, int> GetTranslationWindowsToLinux()
{
Dictionary<int, int> trans = new Dictionary<int, int>();
for (int buc = 0; buc < 256; buc++)
{
int lin = linux_hid_keyboard[buc];
int win = windows_hid_keyboard[buc];
if (lin != 255 && win != 255 && !trans.ContainsKey(win))
trans.Add(win, lin);
}
return trans;
}
public static Dictionary<int, int> GetTranslationLinuxToWindows()
{
Dictionary<int, int> trans = new Dictionary<int, int>();
for (int buc = 0; buc < 256; buc++)
{
int lin = linux_hid_keyboard[buc];
int win = windows_hid_keyboard[buc];
if (lin != 255 && win != 255 && !trans.ContainsKey(lin))
trans.Add(lin, win);
}
return trans;
}
Hope it helps someone in my same situation.
I'm writing code for an embedded device using .Net Micro Framework, I have a set of calculated integers as shown by the link below which I need to access but I'd like it if this information could be minimized somehow, basically I have a list of numbers ranging from 150 - 4, each number has an adjacent list of numbers that it can be linked to, ex 150 can be linked to 149, 148, 146, 144, 142 etc, some numbers (150 to 4) share linked numbers 150 & 149 share 91 as an example as well as many others, so I'm thinking there must be someway of representing all these numbers were the memory footprint is reduced since there are a lot of share linked numbers, any suggestions would be great.
https://www.dropbox.com/s/qqn8097571r9s27/LinkedNumbers.txt
Example Data
150 = 149, 148, 146, 144, 142, 140, 138, 136, 134, 99, 93, 92, 91, 87
149 = 148, 147, 145, 143, 141, 139, 137, 135, 133, 131, 129, 103, 102, 97, 96, 93, 92, 91
148 = 147, 146, 145, 144, 142, 140, 138, 136, 134, 132, 99, 97, 94, 92, 91, 88, 87, 86
147 = 146, 145, 144, 143, 141, 139, 137, 135, 133, 131, 95, 91, 90, 89, 88
Store each row as a 19 byte bit sequence where a one represents an entry in your list and a zero represents a value not in your list.
For example, your first entry is:-
150 = 149, 148, 146, 144, 142, 140, 138, 136, 134, 99, 93, 92, 91, 87,
83, 79, 77, 65, 63, 59, 55, 54, 53, 51, 49, 48, 47, 46, 45, 44,
43, 42, 41, 40, 39, 38, 37, 35, 34, 33, 31, 29, 27, 25, 23, 22,
21, 19, 18, 17, 14, 13, 12, 11, 9, 8, 7, 6, 5, 4, 3, 2
can be store as:-
150 = 0,0,1,1,1,1,1,1,1,1,0,1,1,1,1,0,0,1,1,1,0.....
where the first bit is represents 0, the second represents 1, etc.
You can improve the efficiency of the storage by decreasing the number of bits used for each row, but the computation of the bit to check becomes a bit more complex. Using this, the whole lot can be stored in 1416 bytes.
If the maximum decrement between two sequential numbers doesn't exceed, for instance, 64, then you can store each sequence as a list of 6-bit unsigned decrement values.
Example:
150 = 149, 148, 146, 144, 142, 140, 138, 136, 134, 99, 93, 92, 91, 87
Becomes a:
150 = 1, 1, 2, 2, 2, 2, 2, 2, 2, 35, 6, 1, 1, 4
I understand WP7 support limited number of language as Culture for localization BUT Silverlight itself support more language so my question is Can I make culture info take arguments of "any from supported language" and in resource file string write my own "needed" to support language in my app in this case it's Arabic language ??
in other words file resource.fr.resx and supply strings with Arabic data ?
Will it pass for certification?
For Windows Phone 7 i wrote a mapping function which maps the Arabic encoding[windows-1256] into default WP7 encoding.
public static string ConvertToArabic(string s)
{
short[] mapping = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 8364, 1662, 8218, 402, 8222, 8230, 8224, 8225, 710, 8240, 1657, 8249, 338, 1670, 1688, 1672, 1711, 8216, 8217, 8220, 8221, 8226, 8211, 8212, 1705, 8482, 1681, 8250, 339, 8204, 8205, 1722, 160, 1548, 162, 163, 164, 165, 166, 167, 168, 169, 1726, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 1563, 187, 188, 189, 190, 1567, 1729, 1569, 1570, 1571, 1572, 1573, 1574, 1575, 1576, 1577, 1578, 1579, 1580, 1581, 1582, 1583, 1584, 1585, 1586, 1587, 1588, 1589, 1590, 215, 1591, 1592, 1593, 1594, 1600, 1601, 1602, 1603, 224, 1604, 226, 1605, 1606, 1607, 1608, 231, 232, 233, 234, 235, 1609, 1610, 238, 239, 1611, 1612, 1613, 1614, 244, 1615, 1616, 247, 1617, 249, 1618, 251, 252, 8206, 8207, 1746 };
string str = string.Empty;
for (int ix = 0; ix < s.Length; ++ix)
{
str = str + (char)mapping[s[ix]];
}
return str;
}
This worked for me perfectly.
Windows Phone 7 does not support Arabic character sets (But Windows Phone 8 will).
Some applications have worked around this with custom fonts but this approach will require lots of careful testing.
I have a list of elements which can be easily compared using Equals(). I have to shuffle the list, but the shuffle must satisfy one condition:
The i'th element shuffledList[i] must not equal elements at i +/- 1 nor elements at i +/- 2. The list should be considered circular; that is, the last element in the list is followed by the first, and vice versa.
Also, if possible, I would like to check if the shuffle is even possible.
Note:
I'm using c# 4.0.
EDIT:
Based on some responses, I will explain a little more:
The list wont have more than 200 elements, so there isn't a real need for good performance. If it takes 2 secs to calculate it it's not the best thing, but it's not the end of the world either. The shuffled list will be saved, and unless the real list changes, the shuffled list will be used.
Yes, it's a "controlled" randomness, but I expect that severals run on this method would return different shuffled lists.
I will make further edits after I try some of the responses below.
EDIT 2:
Two sample list that fails with Sehe's implementation (Both have solutions):
Sample1:
`List<int> list1 = new List<int>{0,1,1,1,2,2,2,3,3,3,4,4,4,5,5,6,6,6,7,7,7,7,8,8,8,8,9,9,9,9,9,10};`
Possible solution:
List<int> shuffledList1 = new List<int>
{9,3,1,4,7,9,2,6,8,1,4,9,2,0,6,5,7,8,4,3,10,9,6,7,8,5,3,9,1,2,7,8}
Sample 2:
`List<int> list2 = new List<int> {0,1,1,2,2,2,3,3,4,4,4,4,5,5,5,6,6,6,7,7,7,7,8,8,8,8,8,9,9,9,9,10};`
Verify: I'm using this method, that it's not the most eficient nor elegant piece of code I've made, but it does it's work:
public bool TestShuffle<T>(IEnumerable<T> input)
{
bool satisfied = true;
int prev1 = 0; int prev2 = 0;
int next1 = 0; int next2 = 0;
int i = 0;
while (i < input.Count() && satisfied)
{
prev1 = i - 1; prev2 = i - 2; next1 = i + 1; next2 = i + 2;
if (i == 0)
{
prev1 = input.Count() - 1;
prev2 = prev1 - 1;
}
else if (i == 1)
prev2 = input.Count() - 1;
if (i == (input.Count() - 1))
{
next1 = 0;
next2 = 1;
}
if (i == (input.Count() - 2))
next2 = 0;
satisfied =
(!input.ElementAt(i).Equals(input.ElementAt(prev1)))
&& (!input.ElementAt(i).Equals(input.ElementAt(prev2)))
&& (!input.ElementAt(i).Equals(input.ElementAt(next1)))
&& (!input.ElementAt(i).Equals(input.ElementAt(next2)))
;
if (satisfied == false)
Console.WriteLine("TestShuffle fails at " + i);
i++;
}
return satisfied;
}
EDIT 3:
Another test input that fails sometimes:
List<int> list3 = new List<int>(){0,1,1,2,2,3,3,3,4,4,4,5,5,5,5,6,6,6,6,7,7,7,8,8,8,8,9,9,9,9,10,10};
Optimized version
To my disappointment, my optimized function only runs 7x faster than the LINQ 'straightforward' version. Unoptimized LINQ 1m43s Optimized 14.7s.
linux 32-bit
Mono 2.11 (C# 4.0) compiler with -optimize+,
1,000,000 TESTITERATIONS
VERBOSE not #define-d
What has been optimized:
assume arrays for input and output
work in-place on input array
'manually' analyze runs of identical values, instead of using GroupBy (using ValueRun struct)
have these ValueRun structs in an Array instead of Enumerable (List); sort/shuffle in-place
use unsafe blocks and pointers (no discernable difference...)
use modulo indexing instead of MAGIC Linq code
generate the output by iterative append instead of nested LINQ. This probably had the most effect. Actually, it would be even better when we could shortcut the ValueRuns that have a countruns collection is being ordered by this count, it seemed pretty easy to do; however, the transposed indexing (needed for the cyclic constraints) is complicating things. The gain of somehow applying this optimization anyway will be bigger with larger inputs and many unique values and some highly duplicated values.
Here is the code with optimized version. _Additional speed gain can be had by removing the seeding of RNGs; these are only there to make it possible to regression test the output.
[... old code removed as well ...]
Original response (partial)
If I'm getting you right, you are trying to devise a shuffle that prevents duplicates from ending up consecutive in the output (with a minimum interleave of 2 elements).
This is not solvable in the general case. Imagine an input of only identical elements :)
Update: Troubled state of affairs
Like I mention in the notes, I think I wasn't on the right track all the time. Either I should invoke graph theory (anyone?) or use a simple 'bruteforcey' algorithm instead, much a long Erick's suggestion.
Anyway, so you can see what I've been up to, and also what the problems are (enable the randomized samples to quickly see the problems):
#define OUTPUT // to display the testcase results
#define VERIFY // to selfcheck internals and verify results
#define SIMPLERANDOM
// #define DEBUG // to really traces the internals
using System;
using System.Linq;
using System.Collections.Generic;
public static class Q5899274
{
// TEST DRIVER CODE
private const int TESTITERATIONS = 100000;
public static int Main(string[] args)
{
var testcases = new [] {
new [] {0,1,1,2,2,2,3,3,4,4,4,4,5,5,5,6,6,6,7,7,7,7,8,8,8,8,8,9,9,9,9,10},
new [] {0,1,1,1,2,2,2,3,3,3,4,4,4,5,5,6,6,6,7,7,7,7,8,8,8,8,9,9,9,9,9,10},
new [] { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41, 42, 42, 42, },
new [] {1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4},
}.AsEnumerable();
// // creating some very random testcases
// testcases = Enumerable.Range(0, 10000).Select(nr => Enumerable.Range(GROUPWIDTH, _seeder.Next(GROUPWIDTH, 400)).Select(el => _seeder.Next(-40, 40)).ToArray());
foreach (var testcase in testcases)
{
// _seeder = new Random(45); for (int i=0; i<TESTITERATIONS; i++) // for benchmarking/regression
{
try
{
var output = TestOptimized(testcase);
#if OUTPUT
Console.WriteLine("spread\t{0}", string.Join(", ", output));
#endif
#if VERIFY
AssertValidOutput(output);
#endif
} catch(Exception e)
{
Console.Error.WriteLine("Exception for input {0}:", string.Join(", ", testcase));
Console.Error.WriteLine("Sequence length {0}: {1} groups and remainder {2}", testcase.Count(), (testcase.Count()+GROUPWIDTH-1)/GROUPWIDTH, testcase.Count() % GROUPWIDTH);
Console.Error.WriteLine("Analysis: \n\t{0}", string.Join("\n\t", InternalAnalyzeInputRuns(testcase)));
Console.Error.WriteLine(e);
}
}
}
return 0;
}
#region Algorithm Core
const int GROUPWIDTH = 3; /* implying a minimum distance of 2
(GROUPWIDTH-1) values in between duplicates
must be guaranteed*/
public static T[] TestOptimized<T>(T[] input, bool doShuffle = false)
where T: IComparable<T>
{
if (input.Length==0)
return input;
var runs = InternalAnalyzeInputRuns(input);
#if VERIFY
CanBeSatisfied(input.Length, runs); // throws NoValidOrderingExists if not
#endif
var transpositions = CreateTranspositionIndex(input.Length, runs);
int pos = 0;
for (int run=0; run<runs.Length; run++)
for (int i=0; i<runs[run].runlength; i++)
input[transpositions[pos++]] = runs[run].value;
return input;
}
private static ValueRun<T>[] InternalAnalyzeInputRuns<T>(T[] input)
{
var listOfRuns = new List<ValueRun<T>>();
Array.Sort(input);
ValueRun<T> current = new ValueRun<T> { value = input[0], runlength = 1 };
for (int i=1; i<=input.Length; i++)
{
if (i<input.Length && input[i].Equals(current.value))
current.runlength++;
else
{
listOfRuns.Add(current);
if (i<input.Length)
current = new ValueRun<T> { value = input[i], runlength = 1 };
}
}
#if SIMPLERANDOM
var rng = new Random(_seeder.Next());
listOfRuns.ForEach(run => run.tag = rng.Next()); // this shuffles them
#endif
var runs = listOfRuns.ToArray();
Array.Sort(runs);
return runs;
}
// NOTE: suboptimal performance
// * some steps can be done inline with CreateTranspositionIndex for
// efficiency
private class NoValidOrderingExists : Exception { public NoValidOrderingExists(string message) : base(message) { } }
private static bool CanBeSatisfied<T>(int length, ValueRun<T>[] runs)
{
int groups = (length+GROUPWIDTH-1)/GROUPWIDTH;
int remainder = length % GROUPWIDTH;
// elementary checks
if (length<GROUPWIDTH)
throw new NoValidOrderingExists(string.Format("Input sequence shorter ({0}) than single group of {1})", length, GROUPWIDTH));
if (runs.Length<GROUPWIDTH)
throw new NoValidOrderingExists(string.Format("Insufficient distinct values ({0}) in input sequence to fill a single group of {1})", runs.Length, GROUPWIDTH));
int effectivewidth = Math.Min(GROUPWIDTH, length);
// check for a direct exhaustion by repeating a single value more than the available number of groups (for the relevant groupmember if there is a remainder group)
for (int groupmember=0; groupmember<effectivewidth; groupmember++)
{
int capacity = remainder==0? groups : groups -1;
if (capacity < runs[groupmember].runlength)
throw new NoValidOrderingExists(string.Format("Capacity exceeded on groupmember index {0} with capacity of {1} elements, (runlength {2} in run of '{3}'))",
groupmember, capacity, runs[groupmember].runlength, runs[groupmember].value));
}
// with the above, no single ValueRun should be a problem; however, due
// to space exhaustion duplicates could end up being squeezed into the
// 'remainder' group, which could be an incomplete group;
// In particular, if the smallest ValueRun (tail) has a runlength>1
// _and_ there is an imcomplete remainder group, there is a problem
if (runs.Last().runlength>1 && (0!=remainder))
throw new NoValidOrderingExists("Smallest ValueRun would spill into trailing incomplete group");
return true;
}
// will also verify solvability of input sequence
private static int[] CreateTranspositionIndex<T>(int length, ValueRun<T>[] runs)
where T: IComparable<T>
{
int remainder = length % GROUPWIDTH;
int effectivewidth = Math.Min(GROUPWIDTH, length);
var transpositions = new int[length];
{
int outit = 0;
for (int groupmember=0; groupmember<effectivewidth; groupmember++)
for (int pos=groupmember; outit<length && pos<(length-remainder) /* avoid the remainder */; pos+=GROUPWIDTH)
transpositions[outit++] = pos;
while (outit<length)
{
transpositions[outit] = outit;
outit += 1;
}
#if DEBUG
int groups = (length+GROUPWIDTH-1)/GROUPWIDTH;
Console.WriteLine("Natural transpositions ({1} elements in {0} groups, remainder {2}): ", groups, length, remainder);
Console.WriteLine("\t{0}", string.Join(" ", transpositions));
var sum1 = string.Join(":", Enumerable.Range(0, length));
var sum2 = string.Join(":", transpositions.OrderBy(i=>i));
if (sum1!=sum2)
throw new ArgumentException("transpositions do not cover range\n\tsum1 = " + sum1 + "\n\tsum2 = " + sum2);
#endif
}
return transpositions;
}
#endregion // Algorithm Core
#region Utilities
private struct ValueRun<T> : IComparable<ValueRun<T>>
{
public T value;
public int runlength;
public int tag; // set to random for shuffling
public int CompareTo(ValueRun<T> other) { var res = other.runlength.CompareTo(runlength); return 0==res? tag.CompareTo(other.tag) : res; }
public override string ToString() { return string.Format("[{0}x {1}]", runlength, value); }
}
private static /*readonly*/ Random _seeder = new Random(45);
#endregion // Utilities
#region Error detection/verification
public static void AssertValidOutput<T>(IEnumerable<T> output)
where T:IComparable<T>
{
var repl = output.Concat(output.Take(GROUPWIDTH)).ToArray();
for (int i=1; i<repl.Length; i++)
for (int j=Math.Max(0, i-(GROUPWIDTH-1)); j<i; j++)
if (repl[i].Equals(repl[j]))
throw new ArgumentException(String.Format("Improper duplicate distance found: (#{0};#{1}) out of {2}: value is '{3}'", j, i, output.Count(), repl[j]));
}
#endregion
}
Your requirements eliminate the real shuffling alternative: there is no randomness, or there is controlled randomness.
Here is one special approach
Sort the original list L
Find the mode M, and its frequency n
If n is even, n++.
Create k (= 5*n - 1) lists (prime to n, and 5 times n) L1 through Lk
(5 is chosen to avoid two previous elements and two next elements)
Assign all elements into the k lists in round robin fashion
Individually shuffle all k lists.
Collate the contents of k lists in the following order:
a. pick randomly +5 or -5 as x.
b. pick a random number j.
c. repeat k times:
i. add all contents from Lj.
ii. j <- (j + x) mod k
[5, 6, 7, 7, 8, 8, 9, 10, 12, 13, 13, 14, 14, 14, 17, 18, 18, 19, 19, 20, 21, 21, 21, 21, 24, 24, 26, 26, 26, 27, 27, 27, 29, 29, 30, 31, 31, 31, 31, 32, 32, 32, 33, 35, 35, 37, 38, 39, 40, 42, 43, 44, 44, 46, 46, 47, 48, 50, 50, 50, 50, 51, 52, 53, 54, 55, 56, 57, 57, 58, 60, 60, 60, 61, 62, 63, 63, 64, 64, 65, 65, 65, 68, 71, 71, 72, 72, 73, 74, 74, 74, 74, 75, 76, 76, 76, 77, 77, 77, 78, 78, 78, 79, 79, 80, 81, 82, 86, 88, 88, 89, 89, 90, 91, 92, 92, 92, 93, 93, 94, 94, 95, 96, 99, 99, 100, 102, 102, 103, 103, 105, 106, 106, 107, 108, 113, 115, 116, 118, 119, 123, 124, 125, 127, 127, 127, 128, 131, 133, 133, 134, 135, 135, 135, 137, 137, 137, 138, 139, 141, 143, 143, 143, 145, 146, 147, 153, 156, 157, 158, 160, 164, 166, 170, 173, 175, 181, 181, 184, 185, 187, 188, 190, 200, 200, 215, 217, 234, 238, 240]
Frequency of mode = 4, so 19 slots (#0 - #18)
0: [7, 21, 32, 50, 65, 77, 93, 115, 137, 173]
1: [8, 21, 33, 51, 65, 78, 93, 116, 137, 175]
2: [8, 24, 35, 52, 65, 78, 94, 118, 138, 181]
3: [9, 24, 35, 53, 68, 78, 94, 119, 139, 181]
4: [10, 26, 37, 54, 71, 79, 95, 123, 141, 184]
5: [12, 26, 38, 55, 71, 79, 96, 124, 143, 185]
6: [13, 26, 39, 56, 72, 80, 99, 125, 143, 187]
7: [13, 27, 40, 57, 72, 81, 99, 127, 143, 188]
8: [14, 27, 42, 57, 73, 82, 100, 127, 145, 190]
9: [14, 27, 43, 58, 74, 86, 102, 127, 146, 200]
10: [14, 29, 44, 60, 74, 88, 102, 128, 147, 200]
11: [17, 29, 44, 60, 74, 88, 103, 131, 153, 215]
12: [18, 30, 46, 60, 74, 89, 103, 133, 156, 217]
13: [18, 31, 46, 61, 75, 89, 105, 133, 157, 234]
14: [19, 31, 47, 62, 76, 90, 106, 134, 158, 238]
15: [19, 31, 48, 63, 76, 91, 106, 135, 160, 240]
16: [5, 20, 31, 50, 63, 76, 92, 107, 135, 164]
17: [6, 21, 32, 50, 64, 77, 92, 108, 135, 166]
18: [7, 21, 32, 50, 64, 77, 92, 113, 137, 170]
Shuffling individual lists, and picking lists 5 slots apart (start randomly at #16):
16: [31, 135, 92, 76, 107, 5, 164, 63, 20, 50]
2: [52, 24, 35, 78, 181, 8, 138, 94, 118, 65]
7: [57, 143, 99, 81, 40, 13, 127, 72, 188, 27]
12: [46, 30, 60, 89, 133, 74, 156, 18, 103, 217]
17: [64, 50, 135, 92, 21, 32, 108, 77, 166, 6]
3: [9, 94, 181, 119, 24, 35, 139, 68, 53, 78]
8: [145, 27, 14, 57, 42, 100, 190, 82, 73, 127]
13: [89, 18, 75, 61, 157, 234, 133, 105, 31, 46]
18: [113, 21, 7, 92, 64, 32, 137, 50, 170, 77]
4: [71, 10, 37, 26, 123, 54, 184, 79, 95, 141]
9: [27, 74, 86, 14, 102, 146, 127, 43, 58, 200]
14: [62, 106, 158, 134, 19, 47, 238, 31, 76, 90]
0: [7, 77, 65, 21, 50, 93, 173, 115, 32, 137]
5: [96, 79, 26, 185, 12, 71, 124, 143, 55, 38]
10: [29, 14, 147, 60, 128, 88, 74, 44, 102, 200]
15: [106, 240, 63, 48, 91, 19, 160, 31, 76, 135]
1: [65, 33, 21, 51, 137, 8, 175, 93, 116, 78]
6: [143, 26, 13, 56, 99, 72, 39, 80, 187, 125]
11: [103, 88, 29, 60, 74, 44, 17, 153, 131, 215]
[31, 135, 92, 76, 107, 5, 164, 63, 20, 50, 52, 24, 35, 78, 181, 8, 138, 94, 118, 65, 57, 143, 99, 81, 40, 13, 127, 72, 188, 27, 46, 30, 60, 89, 133, 74, 156, 18, 103, 217, 64, 50, 135, 92, 21, 32, 108, 77, 166, 6, 9, 94, 181, 119, 24, 35, 139, 68, 53, 78, 145, 27, 14, 57, 42, 100, 190, 82, 73, 127, 89, 18, 75, 61, 157, 234, 133, 105, 31, 46, 113, 21, 7, 92, 64, 32, 137, 50, 170, 77, 71, 10, 37, 26, 123, 54, 184, 79, 95, 141, 27, 74, 86, 14, 102, 146, 127, 43, 58, 200, 62, 106, 158, 134, 19, 47, 238, 31, 76, 90, 7, 77, 65, 21, 50, 93, 173, 115, 32, 137, 96, 79, 26, 185, 12, 71, 124, 143, 55, 38, 29, 14, 147, 60, 128, 88, 74, 44, 102, 200, 106, 240, 63, 48, 91, 19, 160, 31, 76, 135, 65, 33, 21, 51, 137, 8, 175, 93, 116, 78, 143, 26, 13, 56, 99, 72, 39, 80, 187, 125, 103, 88, 29, 60, 74, 44, 17, 153, 131, 215]
It can be accomplished with a simple, two-step process. First use a Fisher-Yates (Knuth) shuffle in place. Then iterate over the list once, copying its elements to a new list. As you encounter an element, insert it into the first legal position in your new list. (This will be much more efficient with a linked list than with an array as your destination.) If there are no legal position to insert to, the instance of the problem is unsolvable. If you manage to fill your destination list, problem solved. This will take O(n) in the best case and O(n^2) in the worst.
permute valid 5 in list, if none unsolvable
remove permutation from list
create a cycle graph of that 5
order the list(remaining list) by count of valid positions in new graph (if you dont do this you can end up with a wrong non solvable, because putting items that can go on more positions increases the possible position count of items with less)
continue picking items in the list, add items to cycle graph at valid positions
if there is no valid position in graph or graph cannot be created continue on next
if graph created revert back to start iteration where graph has been created
continue creating other graphs
save all full graphs in a list
pick a random from that list
if list is empty unsolvable