Int Array Reorder with Even Distribution in C#? - c#

12,13,14,15,16,19,19,19,19
to
12,19,13,19,14,19,15,19,16
Hey all. Can anyone point me to clues/samples on how to distribute the first array of Int32 values, where a bunch of 19 values were appended, to the second where the 19 values are fairly evenly interspersed in the array?
I am not looking for a random shuffling, as in this example #19 could still appear consecutively if there was randomization. I want to make sure that #19 is placed in between the other numbers in a predictable pattern.
The use-case for this is something like teams taking turns presenting a topic: teams 12-16 each present once and then team #19 shows up but should not show their topic four times in a row, they should show their topic in between the other teams.
Later, if twelve values of 7 are added to the array, then they will also have to be evenly distributed into the sequence as well, the array would be 21 elements but the same rule that neither #19 or #7 should have consecutive showings.
I thought there might be something in Math.NET library that would do this, but I did not find anything. Using C# on .NET Framework 4.7.
Thanks.

Details on the following method that evenly (mostly) distributes the duplicates in your list. Duplicates can be anywhere in your list, they will be distributed.
Create a dictionary of all numbers and keep track of the number of times they appear in the list
Use a new list without any duplicates. For Each number that has duplicates, spread it over the size of this new list. Each time the distribution is even.
public static List<int> EvenlyDistribute(List<int> list)
{
List<int> original = list;
Dictionary<int, int> dict = new Dictionary<int, int>();
list.ForEach(x => dict[x] = dict.Keys.Contains(x) ? dict[x] + 1 : 1);
list = list.Where(x => dict[x] == 1).ToList();
foreach (int key in dict.Where(x => x.Value > 1).Select(x => x.Key))
{
int iterations = original.Where(x => x == key).Count();
for (int i = 0; i < iterations; i++)
list.Insert((int)Math.Ceiling((decimal)((list.Count + iterations) / iterations)) * i, key);
}
return list;
}
Usage in main:
List<int> test = new List<int>() {11,11,11,13,14,15,16,17,18,19,19,19,19};
List<int> newList = EvenlyDistribute(test);
Output
19,11,13,19,14,11,19,15,16,19,11,17,18

Here's how to do this.
var existing = new[] { 12, 13, 14, 15, 16 };
var additional = new [] { 19, 19, 19, 19 };
var lookup =
additional
.Select((x, n) => new { x, n })
.ToLookup(xn => xn.n * existing.Length / additional.Length, xn => xn.x);
var inserted =
existing
.SelectMany((x, n) => lookup[n].StartWith(x))
.ToArray();
This gives me results like 12, 19, 13, 19, 14, 19, 15, 19, 16.
The only thing that this won't do is insert a value in the first position, but otherwise it does evenly distribute the values.

In case random distribution is enough the following code is sufficient:
static void MixArray<T>(T[] array)
{
Random random = new Random();
int n = array.Length;
while (n > 1)
{
n--;
int k = random.Next(n + 1);
T value = array[k];
array[k] = array[n];
array[n] = value;
}
}
For instance:
int[] input = new int[]{12,13,14,15,16,19,19,19,19};
MixArray<int>(input);
In case you require precise evenly distribution while retaining the order of the elements, to following code will do the job:
public static T[] EvenlyDistribute<T>(T[] existing, T[] additional)
{
if (additional.Length == 0)
return existing;
if (additional.Length > existing.Length)
{
//switch arrays
T[] temp = additional;
additional = existing;
existing = temp;
}
T[] result = new T[existing.Length + additional.Length];
List<int> distribution = new List<int>(additional.Length);
double ratio = (double)(result.Length-1) / (additional.Length);
double correction = -1;
if (additional.Length == 1)
{
ratio = (double)result.Length / 2;
correction = 0;
}
double sum = 0;
for (int i = 0; i < additional.Length; i++)
{
sum += ratio;
distribution.Add(Math.Max(0, (int)(sum+correction)));
}
int existing_added = 0;
int additional_added = 0;
for (int i = 0; i < result.Length; i++)
{
if (additional_added == additional.Length)
result[i] = existing[existing_added++];
else
if (existing_added == existing.Length)
result[i] = additional[additional_added++];
else
{
if (distribution[additional_added] <= i)
result[i] = additional[additional_added++];
else
result[i] = existing[existing_added++];
}
}
return result;
}
For instance:
int[] existing = new int[] { 12, 13, 14, 15, 16};
int[] additional = new int[] { 101, 102, 103, 104};
int[] result = EvenlyDistribute<int>(existing, additional);
//result = 12, 101, 13, 102, 14, 103, 15, 104, 16

Related

Counting huge permutations - counting elements and getting nth element

I am using this library for combinatorics:
https://github.com/eoincampbell/combinatorics/
What I need is to find n-th permutation and count elements of fairly large sets (up to about 30 elements), but I get stopped in my tracks before even starting, check out this code:
int[] testSet = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21};
var permutation = new Permutations<int>(testSet);
var test = permutation.Count;
Everything works peachy just until 20 element large set, once I add 21st, permutations stop working right, eg.
here is what permutation.Count returns:
-4249290049419214848
which is far from being the right number.
I am assuming that it all boils down to how huge numbers I use - overflowing ints/longs that library uses. That is why, I am asking for an advice - is there a library? approach? or a fairly quick to implement way to have combinatorics work on bigintegers?
Thanks!
Get the number of possible permuations.
The number of permutations is defined by nPr or n over r
n!
P(n,r) = --------
(n - r)!
Where:
n = Number of objects
r = the size of the result set
In your example, you want to get all permutations of a given list. In this case n = r.
public static BigInteger CalcCount(BigInteger n, BigInteger r)
{
BigInteger result = n.Factorial() / (n - r).Factorial();
return result;
}
public static class BigIntExtensions
{
public static BigInteger Factorial(this BigInteger integer)
{
if(integer < 1) return new BigInteger(1);
BigInteger result = integer;
for (BigInteger i = 1; i < integer; i++)
{
result = result * i;
}
return result;
}
}
Get the nTh permutation
This one depends on how you create/enumerate the permutations. Usually to generate any permutation you do not need to know all previous permutations. In other words, creating a permutation could be a pure function, allowing you to directly create the nTh permutation, without creating all possible ones.
This, however, depends on the algorithms used. But will potentially be a lot faster to create the permutation only when needed (in contrast to creating all possible permutations up front -> performance and very memory heavy).
Here is a great discussion on how to create permutations without needing to calculate the previous ones: https://stackoverflow.com/a/24257996/1681616.
This is too long for a comment, but wanted to follow up on #Iqon's solution above. Below is an algorithm that retrieves the nth lexicographical permutation:
public static int[] nthPerm(BigInteger myIndex, int n, int r, BigInteger total)
{
int j = 0, n1 = n;
BigInteger temp, index1 = myIndex;
temp = total ;
List<int> indexList = new List<int>();
for (int k = 0; k < n; k++) {
indexList.Add(k);
}
int[] res = new int[r];
for (int k = 0; k < r; k++, n1--) {
temp /= n1;
j = (int) (index1 / temp);
res[k] = indexList[j];
index1 -= (temp * j);
indexList.RemoveAt(j);
}
return res;
}
Here is a test case and the result of calling nthPerm using the code provided by #Iqon.
public static void Main()
{
int[] testSet = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21};
BigInteger numPerms, n, r;
n = testSet.Length;
r = testSet.Length;
numPerms = CalcCount(n, r);
Console.WriteLine(numPerms);
BigInteger testIndex = new BigInteger(1234567890987654321);
int[] myNthIndex = nthPerm(testIndex, (int) n, (int) r, numPerms);
int[] myNthPerm = new int[(int) r];
for (int i = 0; i < (int) r; i++) {
myNthPerm[i] = testSet[myNthIndex[i]];
}
Console.WriteLine(string.Join(",", myNthPerm));
}
// Returns 1,12,4,18,20,19,7,5,16,11,6,8,21,15,13,2,14,9,10,17,3
Here is a link to ideone with working code.
You can useJNumberTools
List<String> list = new ArrayList<>();
//add elements to list;
JNumberTools.permutationsOf(list)
.uniqueNth(1000_000_000) //next 1 billionth permutation
.forEach(System.out::println);
This API will generate the next nth permutation directly in lexicographic order. So you can even generate next billionth permutation of 100 items.
for generating next nth permutation of given size use:
maven dependency for JNumberTools is:
<dependency>
<groupId>io.github.deepeshpatel</groupId>
<artifactId>jnumbertools</artifactId>
<version>1.0.0</version>
</dependency>

c# Select N elements from list uniformly without shuffle

I want to select K elements uniformly from a list of N elements without shuffling a list. So the algorithm should always produce the same result.
E.g.
int N = 100;
List<int> myList = Enumerable.Range(0, N-1).ToList();
int K = 20;
Here i expect the result: 0, 5, 10, 15, 20, ...
E.g. if K == 50 I expect: 0, 2, 4, 6, 8, ...
But I don't know how to solve it if e.g. K = 53? I still want to take uniformly elements from the list, but in this case we can't simply take each second element from the list. Simple approach would be to shuffle elements and take first K elements from the list, order again the list but in that case the result of algorithm would each time produce different result and i need each time the same result.
Any help would be appreciated.
Find what the indices would be if they were all equidistant, and round to the nearest integer.
int N = 100;
List<int> myList = Enumerable.Range(0, N).ToList();
double K = 53;
List<int> solutions=new List<int>();
for (int i=1;i<=K;i++)
{
solutions.Add(myList[(int)Math.Round(i*(N/K)-1)]);
}
Console.WriteLine(solutions.Count);
Console.WriteLine(string.Join(", ", solutions));
See a sample at: DEMO
EDIT: Math.Floor works better than Math.Round as Math.Round gives the wrong solution to N=5, K=3:
Math.Round->1,2,4
Math.Floor->0,2,4
See a sample at: DEMO2
This problem is exactly same as line drawing algorithm: https://en.wikipedia.org/wiki/Line_drawing_algorithm
In your case, you want to draw a line from (0, 0) to (20, 52)
You could still shuffle the list. Please see the follwing example
var n = 100;
var k = 53;
var originalList = GetListWithNElements(n);
var shuffledList = ShuffleList(originalList);
var firstKElements = GetFirstKElements(k, shuffledList);
[...]
List<int> GetListWithNElements(int n)
{
return Enumerable.Range(0, n-1).ToList();
}
List<int> ShuffleList(List<int> originalList)
{
List<int> copy = originalList.ToList();
List<int> result = new List<int>();
Random randomGenerator = new Random(314159);
while(copy.Any())
{
int currentIndex = randomGenerator.Next(copy.Count);
result.Add(copy[currentIndex]);
copy.RemoveAt(currentIndex);
}
return result;
}
List<int> GetFirstKElements(int k, List<int> shuffledList)
{
return shuffledList.Take(k).ToList();
}
Random is initialized with a constant seed and hence will produce the very same sequence of "random" values each time, hence you will take the same elements each time.
Try following :
static void Main(string[] args)
{
const int N = 100;
{
List<int> myList = Enumerable.Range(0, N - 1).ToList();
int seed = 5;
int numberOfItems = 50;
List<int> results = TakeNItems(myList, seed, numberOfItems);
}
}
static List<int> TakeNItems(List<int> data, int seed, int numberOfItems)
{
Random rand = new Random(seed);
return data.Select((x,i) => new { x = x, index = i, rand = rand.Next()})
.OrderBy(x => x.rand)
.Take(numberOfItems)
.OrderBy(x => x.index)
.Select(x => x.x)
.ToList();
}

Random value without duplicates

Random r = new Random();
int randomvalue = r.Next(1,20);
*
*
if(randomvalue == 1) something
if(randomvalue == 2) something
*
*
if(randomvalue == 19) something
What is the best way to make that randomvalue without repeat? Btw: Its WPF, not console.
Try something like below :
Random randomInstance = new Random();
List<int> NumList = new List<int>();
NumList.AddRange(new int[] { 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 });
int index = randomInstance.Next(0, NumList.Count - 1);
int randomNumber = NumList[index];
NumList.RemoveAt(index);
// This will work
class Program
{
static void Main(string[] args)
{
List<int> i = new List<int>(new int[] { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20});
List<int> r;
r = ShuffleList(i);
}
private static List<E> ShuffleList<E>(List<E> unshuffledList)
{
List<E> sList = new List<E>();
Random r = new Random();
int randomIndex = 0;
while (unshuffledList.Count > 0)
{
randomIndex = r.Next(0, unshuffledList.Count);
sList.Add(unshuffledList[randomIndex]);
unshuffledList.RemoveAt(randomIndex); //remove so wont be added a second time
}
return sList; //return the new shuffled list
}
}
Try this:
var rnd = new Random();
var shuffled =
Enumerable
.Range(1, 20)
.OrderBy(x => rnd.Next())
.ToArray();
using System;
using System.Collections;
using System.Collections.Generic;
namespace SOFAcrobatics
{
public static class Launcher
{
public static void Main ()
{
// 1 to 20 without duplicates
List<Int32> ns = new List<Int32>();
Random r = new Random();
Int32 ph = 0;
while (ns.Count < 20)
{
while (ns.Contains (ph = r.Next(1, 21))) {}
ns.Add(ph);
}
// ns is now populated with random unique numbers (1 to 20)
}
}
}
You need to store random value is the only way to make it unique.
Try this:
Random r = new Random();
public List<int> randomList = new List<int>();
int randomvalue = 0;
Public void newNumber()
{
randomvalue = r.Next(0, 20);
if (!randomList.Contains(randomvalue))
{
randomList.Add(randomvalue);
if(randomvalue == 1)
//do something
if(randomvalue == N)
// do something
}
}
It is not clear what you mean by repeat. If you don't want to see the same number in a row then just keep a hash table or Dictionary<int,int> which keeps the last integer that came out. Then check if the next number is same with last number. If not, remove last number from dictionary and put the current one. If they are same then request another random integer from Random.
var myDictionary = new Dictionary<int,int>;
var randomvalue = r.Next(1,20);
while(myDictionary.ContainsKey(randomvalue))
{
randomvalue = r.Next(1,20);
}
myDictionary.Clear();
myDictionary.Add(randomvalue, 123); //123 is just a number. Doesn't matter.
This guarantees that two same integer will never come consecutively.
NOTE:: Other answers are creative, but "know your data structure". Don't use a list for look up. Hash is what we use for it.

how to find items in list that their sum is less than or equal a number

I have a list of numbers and I want to know which combination of the numbers in the list has the closest sum to a specific number(Target).( if there are many combinations, finding one is enough)
I searched a lot and know that there are lots of solutions here to find the combinations that their sum is equal to specific number, but there were no solution to find the greatest close to that number.
I also wrote a piece of code that loops from zero to "Target" to find the biggest sum, but as this process is so time consuming as it must be calculated for 100,000 lists, i wonder if there is more efficient way using preferably linq.
var List1 = new int[] { 5, 10, 15, 20, 25, 30, 35, 40, 45 };
var target = 40;
int MaxViewers = Convert.ToInt32(txtNoRecordsToAdd.Text);
for (int UserNo = 1; UserNo <= MaxViewers; UserNo++)
{
for (int No = 1; No <= target; No++)
{
var matches = from subset in MyExtensions.SubSetsOf(List1)
where subset.Sum() == target
select subset;
}
}
public static class MyExtensions
{
public static IEnumerable<IEnumerable<T>> SubSetsOf<T>(this IEnumerable<T> source)
{
if (!source.Any())
return Enumerable.Repeat(Enumerable.Empty<T>(), 1);
// Grab the first element off of the list
var element = source.Take(1);
// Recurse, to get all subsets of the source, ignoring the first item
var haveNots = SubSetsOf(source.Skip(1));
// Get all those subsets and add the element we removed to them
var haves = haveNots.Select(set => element.Concat(set));
// Finally combine the subsets that didn't include the first item, with those that did.
return haves.Concat(haveNots);
}
}
Hello let's check a tricky way to achieve this,
var List1 = new int[] { 5, 10, 15, 20, 25, 30, 35, 40, 45 };
var target = 40;
int MaxViewers = Convert.ToInt32(txtNoRecordsToAdd.Text);
var closestSubSet = MyExtensions.SubSetsOf(List1)
.Select(o=> new{ SubSet = o, Sum = o.Sum(x=> x)})
.Select(o=> new{ SubSet = o.SubSet, Sum = o.Sum, FromTarget = (target - o.Sum >= 0 ? target - o.Sum : (target - o.Sum) * -1 ) })
.OrderBy(o=> o.FromTarget).FirstOrDefault();
I know it's tricky i made the 2nd select for some performance reasons (Not Calling Sum Multiple Times But Use It). This should find the closest sum to the target you specify ^^ Have Fun
Optimization:
var List1 = new int[] { 5, 10, 15, 20, 25, 30, 35, 40, 45 };
var target = 40;
int MaxViewers = Convert.ToInt32(txtNoRecordsToAdd.Text);
int minClosestTargetRequired = 0;
int closestSum = int.maxValue;
int closestSetIndex = -1;
var subsets = MyExtensions.SubSetsOf(List1);
for(var c = 0 ; c < subsets.Count() ; c++)
{
int currentSum = subsets[c].Sum(o=> o);
if(currentSum < closestSum)
{
closestSum = currentSum;
closestSetIndex = c;
}
if(closestSum <= minClosestTargetRequired)
break;
}
Console.WriteLine("Closest Sum Is {0} In Set Index {1},closestSum,closestSetIndex);

Merging two lists into one and sorting the items

Is there a way to merge(union without dupes) two given lists into one and store the items in sorted way by using ONE for loop?
Also, i am looking for a solution which does not makes use of API methods ( like, union, sort etc).
Sample Code.
private static void MergeAndOrder()
{
var listOne = new List<int> {3, 4, 1, 2, 7, 6, 9, 11};
var listTwo = new List<int> {1, 7, 8, 3, 5, 10, 15, 12};
//Without Using C# helper methods...
//ToDo.............................
//Using C# APi.
var expectedResult = listOne.Union(listTwo).ToList();
expectedResult.Sort();//Output: 1,2,3,4,5,6,7,8,9,10,11,12,15
//I need the same result without using API methods, and that too by iterating over items only once.
}
PS: I have been asked this question in an interview, but couldn't find answer as yet.
Why can't you use the api methods? Re-inventing the wheel is dumb. Also, it's the .ToList() call that's killing you. Never call .ToList() or .ToArray() until you absolutely have to, because they break your lazy evaluation.
Do it like this and you'll enumerate the lists with the minimum amount necessary:
var expectedResult = listOne.Union(listTwo).OrderBy(i => i);
This will do the union in one loop using a hashset, and lazy execution means the base-pass for the sort will piggyback on the union. But I don't think it's possible finish the sort in a single iteration, because sorting is not a O(n) operation.
Without the precondition that both lists are sorted before the merge + sort operation, you can't do this in O(n) time (or "using one loop").
Add that precondition and the problem is very easy.
Keep two iterators, one for each list. On each loop, compare the element from each list and choose the smaller. Increment that list's iterator. If the element you are about to insert in the final list is already the last element in that list, skip the insert.
In pseudocode:
List a = { 1, 3, 5, 7, 9 }
List b = { 2, 4, 6, 8, 10 }
List result = { }
int i=0, j=0, lastIndex=0
while(i < a.length || j < b.length)
// If we're done with a, just gobble up b (but don't add duplicates)
if(i >= a.length)
if(result[lastIndex] != b[j])
result[++lastIndex] = b[j]
j++
continue
// If we're done with b, just gobble up a (but don't add duplicates)
if(j >= b.length)
if(result[lastIndex] != a[i])
result[++lastIndex] = a[i]
i++
continue
int smallestVal
// Choose the smaller of a or b
if(a[i] < b[j])
smallestVal = a[i++]
else
smallestVal = b[j++]
// Don't insert duplicates
if(result[lastIndex] != smallestVal)
result[++lastIndex] = smallestVal
end while
private static void MergeTwoSortedArray(int[] first, int[] second)
{
//throw new NotImplementedException();
int[] result = new int[first.Length + second.Length];
int i=0 , j=0 , k=0;
while(i < first.Length && j <second.Length)
{
if(first[i] < second[j])
{
result[k++] = first[i++];
}
else
{
result[k++] = second[j++];
}
}
if (i < first.Length)
{
for (int a = i; a < first.Length; a++)
result[k] = first[a];
}
if (j < second.Length)
{
for (int a = j; a < second.Length; a++)
result[k++] = second[a];
}
foreach (int a in result)
Console.Write(a + " ");
Console.WriteLine();
}
Using iterators and streaming interface the task is not that complicated:
class MergeTwoSortedLists
{
static void Main(string[] args) {
var list1 = new List<int?>() {
1,3,5,9,11
};
var list2 = new List<int?>() {
2,5,6,11,15,17,19,29
};
foreach (var c in SortedAndMerged(list1.GetEnumerator(), list2.GetEnumerator())) {
Console.Write(c+" ");
}
Console.ReadKey();
}
private static IEnumerable<int> SortedAndMerged(IEnumerator<int?> e1, IEnumerator<int?> e2) {
e2.MoveNext();
e1.MoveNext();
do {
while (e1.Current < e2.Current) {
if (e1.Current != null) yield return e1.Current.Value;
e1.MoveNext();
}
if (e2.Current != null) yield return e2.Current.Value;
e2.MoveNext();
} while (!(e1.Current == null && e2.Current == null));
}
}
Try this:
public static IEnumerable<T> MergeWith<T>(IEnumerable<T> collection1, IEnumerable<T> collection2,
IComparer<T> comparer)
{
using (var enumerator1 = collection1.GetEnumerator())
using (var enumerator2 = collection2.GetEnumerator())
{
var isMoveNext1 = enumerator1.MoveNext();
var isMoveNext2 = enumerator2.MoveNext();
do
{
while (comparer.Compare(enumerator1.Current, enumerator2.Current) < 0 || !isMoveNext2)
{
if (isMoveNext1)
yield return enumerator1.Current;
else
break;
isMoveNext1 = enumerator1.MoveNext();
}
if (isMoveNext2)
yield return enumerator2.Current;
isMoveNext2 = enumerator2.MoveNext();
} while (isMoveNext1 || isMoveNext2);
}
}
You could write a loop that merges and de-dups the lists and uses a binary-search approach to insert new values into the destination list.
var listOne = new List<int> { 3, 4, 1, 2, 7, 6, 9, 11 };
var listTwo = new List<int> { 1, 7, 8, 3, 5, 10, 15, 12 };
var result = listOne.ToList();
foreach (var n in listTwo)
{
if (result.IndexOf(n) == -1)
result.Add(n);
}
The closest solution I see would be to allocate an array knowing that integers are bounded to some value.
int[] values = new int[ Integer.MAX ]; // initialize with 0
int size1 = list1.size();
int size2 = list2.size();
for( int pos = 0; pos < size1 + size2 ; pos++ )
{
int val = pos > size1 ? list2[ pos-size1 ] : list1[ pos ] ;
values[ val ]++;
}
Then you can argue that you have the sorted array in a "special" form :-) To get a clean sorted array, you need to traverse the values array, skip all position with 0 count, and build the final list.
This will only work for lists of integers, but happily that is what you have!
List<int> sortedList = new List<int>();
foreach (int x in listOne)
{
sortedList<x> = x;
}
foreach (int x in listTwo)
{
sortedList<x> = x;
}
This is using the values in each list as the index position at which to store the value. Any duplicate values will overwrite the previous entry at that index position. It meets the requirement of only one iteration over the values.
It does of course mean that there will be 'empty' positions in the list.
I suspect the job position has been filled by now though.... :-)

Categories