Get Items that sum Closest to Given Value - c#

I have a list of values. lets say they're {1,2,3,4,5}
and I want to find the combination that sums closest to a given value
so for example if i entered 8 then the function could return
either {3,5} or {1,3,4}. either of those would be exact and i would just take the one with the least indexes 3+5
if there isn't a value that is exact for example 9.45 it would return the value
closest without going over the threshold {4,5}
I'm not sure where i would even start with this. i think it may be possible with a linq query...

static IEnumerable<int> GetClosestValues(IReadOnlyList<int> c, int t)
{
var s=0m;
return c.Select((x, i) => i).OrderByDescending(i => c[i]).Where(i => (s += c[i]) <= t).OrderBy(i => i);
}
Seems to work... not optimized though

The key is just to find all permutations of the set, and then filter (less or equal than threshold value) and order (by distance to threshold value, then by size of the set) them via LINQ:
var source = Enumerable.Range(1, 5).Select(x => (double)x).ToArray();
var permutations = Enumerable.Range(1, source.Length)
.SelectMany(x => Utils.GetOrderedPermutations(source, x))
.Dump();
var threshold = 9.45;
var result = permutations
.Select(x => x.ToArray())
.Select(x => new { Set = x, Sum = x.Sum() })
.Where(x => x.Sum <= threshold)
.OrderBy(x => threshold - x.Sum)
.ThenBy(x => x.Set.Length)
.FirstOrDefault()
.Dump();
GetOrderedPermutations is taken from this answer:
public class Utils
{
public static IEnumerable<T> Yield<T>(T value)
{
yield return value;
}
public static IEnumerable<IEnumerable<T>> GetOrderedPermutations<T>(IEnumerable<T> source, int k)
{
if (k == 0) return new[] { Enumerable.Empty<T>() };
int length = source.Count();
if (k == length) return new[] { source };
if (k > length) return Enumerable.Empty<IEnumerable<T>>();
return GetOrderedHelper<T>(source, k, length);
}
private static IEnumerable<IEnumerable<T>> GetOrderedHelper<T>(IEnumerable<T> source, int k, int length)
{
if (k == 0)
{
yield return Enumerable.Empty<T>();
yield break;
}
int i = 0;
foreach (var item in source)
{
if (i + k > length) yield break;
var permutations = GetOrderedHelper<T>(source.Skip(i + 1), k - 1, length - i);
i++;
foreach (var subPerm in permutations)
{
yield return Yield(item).Concat(subPerm);
}
}
}
}

Related

What should i do to get a valid test result (string[]) instead of an WhereEnumerableIterator<string>[] for a method using Linq?

The problem is this:
with given integers a and b, return all the possible combinations in the form
±1±2±3±...±a = b
this is my method for it
public static IEnumerable<string> AllCombinationsWithAAndSumOfB(int a, int b)
{
List<string> list= new List<string>();
Func<string, List<string>> newList = y =>
{
var list = new string[2];
list[0] = y + "+";
list[1] = y + "-";
return list.ToList();
};
Func<int, List<string>> AddingToTheLists = x =>
{
list = list.SelectMany(y => newList(y)).ToList();
return list;
};
Func<char, string, int> CharToInt = (x, g) =>
{
return x == '-' ? -(g.IndexOf(x) + 1) : g.IndexOf(x) + 1;
};
return Enumerable.Range(1, a).SelectMany(AddingToTheLists).Where(x => x.Length == a && x.Sum(y => CharToInt(y, x)) <= b);
}
Now, i'm trying to run a test. This is the test.
[Fact]
public void AllCombinationsWithAAndSumOfBWork()
{
string[] list= { "++-", "+-+", "-+-", "--+", "---" };
var result = AllCombinationsWithAAndSumOfB(3, 0);
Assert.Equal(list, result);
}
The problem is the result of the test:
Result Message:
Assert.Equal() Failure
Expected: String[] ["++-", "+-+", "-+-", "--+", "---"]
Actual: WhereEnumerableIterator<String> []
What should i do to stop getting the result WhereEnumerableIterator [] ?
I'm thinking you want all possible combinations of additions and subtractions for a by-one sequentially increasing ordered list, that is of a length, of integers that sum up to b.
For example: If a = 3, and b = 0, then all combinations of additions and subtractions of each of the integers in the following ordered list: 1, 2, 3 should sum to 0.
If so, then the algorithm can be as follows:
public static IEnumerable<string> AllCombinationsWithAAndSumOfB(int a, int b)
{
var numbers = Enumerable.Range(1, a);
var signCombos = Enumerable
.Range(0, Convert.ToInt32("".PadLeft(a, '1'), 2) + 1)
.Select(e => Convert.ToString(e, 2).PadLeft(a, '0').Replace('0', '-').Replace('1', '+'));
var calc = new DataTable();
return signCombos.Select(
signCombo => new
{
signCombo = signCombo,
formula = String.Join("", signCombo.ToCharArray().Zip(numbers, (s, n) => $"{s}{n}"))
})
.Where(si => ((int)calc.Compute(si.formula, null)) == b)
.Select(si => si.signCombo);
}
This returns
--+
++-
Because:
-1 -2 +3 = 0
+1 +2 -3 = 0

Permutations with repetition

If you can find a better title, please edit.
I will start by saying I've looked at several q&a's on this topic, mainly this one and this article without having found a way to do this:
Given the word "HALLOWEEN" I would like to find all permutations and combinations for all lengths. The first thing I tried was iterating through the below code giving it length of 1 to begin with and continuing until reaching the length of the word (9).
public static IEnumerable<IEnumerable<T>>
GetPermutations<T>(IEnumerable<T> list, int length)
{
if (length == 1) return list.Select(t => new T[] {t});
return GetPermutations(list, length - 1)
.SelectMany(t => list.Where(e => !t.Contains(e)),
(t1, t2) => t1.Concat(new T[] {t2}));
}
This gave me unexpected results as the double 'E' and 'L's were omitted, leaving the final set short.
A simpler example could be 'MOM' {M,O,M} where the final set of outcomes would be:
M
O
MO
OM
MM
MOM
MMO
OMM
Notice that I want to see both 'M's as available, but I don't want to see "MMM" as a result. "MOM" would appear twice in the result due to leaving original order (1,2,3) and swapping positions 1 and 3 (3,2,1) would both result in 'M','O','M' but this character sequence only appears once is the result list (which can be done by a string comparison)
Again, with set {1,1,2,3} I would expect to see:
{1,1}
but NOT {2,2} or {3,3}
Here's another solution that should be clear and easily understandable:
public static IEnumerable<string> GetPermutations(string input)
{
if (string.IsNullOrEmpty(input))
{
return new List<string>();
}
var length = input.Length;
var indices = Enumerable.Range(0, length).ToList();
var permutationsOfIndices = GetNumericalPermutations(indices, length);
var permutationsOfInput = permutationsOfIndices.Select(x => new string(x.Select(y => input[y]).ToArray()))
.Distinct();
return permutationsOfInput;
}
private static List<List<int>> GetNumericalPermutations(List<int> values, int maxLength)
{
if (maxLength == 1)
{
return values.Select(x => new List<int>{x}).ToList();
}
else
{
var permutations = GetNumericalPermutations(values, maxLength - 1);
foreach (var index in values)
{
var newPermutations = permutations.Where(x => !x.Contains(index))
.Select(x => x.Concat(new List<int> { index }))
.Where(x => !permutations.Any(y => y.SequenceEqual(x)))
.Select(x => x.ToList())
.ToList();
permutations.AddRange(newPermutations);
}
return permutations;
}
}
For example, the output for "MOM" is:
M
O
OM
MM
MO
MMO
OMM
MOM
I suggest looking at the permutations of the letter positions 0,1,2,3,4,etc mapping those to letters, and then eliminating the duplicates.
Without changing the GetPermutations function, I added another function to get the permutations of the letter positions, map those result to character strings and then eliminate the duplicates.
public void PermutationsTestMethod()
{
GetPermutationsOfString("MOM").ForEach(v => Debug.Print(v));
}
public List<string> GetPermutationsOfString(string value)
{
var resultList = new List<string>();
for (var i = 1; i <= value.Length; i++)
{
var permutations = GetPermutations(Enumerable.Range(0, value.Length), i);
resultList.AddRange(
permutations
.Select(v => new string(v.Select(z => value[z]).ToArray()))
.Distinct()
);
}
return resultList;
}
public static IEnumerable<IEnumerable<T>> GetPermutations<T>(IEnumerable<T> list, int length)
{
if (length == 1) return list.Select(t => new T[] { t });
return GetPermutations(list, length - 1)
.SelectMany(t => list.Where(e => !t.Contains(e)),
(t1, t2) => t1.Concat(new T[] { t2 }));
}
This works fine:
Func<string, IEnumerable<string>> getAllSubsets = null;
getAllSubsets = x =>
(x == null || !x.Any())
? Enumerable.Empty<string>()
: (x.Length > 1
? getAllSubsets(x.Substring(1))
.SelectMany(y => new [] { y, x.Substring(0, 1) + y })
: new [] { "", x.Substring(0, 1) });
So given getAllSubsets("ABC") I get:
"", "A", "B", "AB", "C", "AC", "BC", "ABC"
And, for your "MOM" example I get:
"", "M", "O", "MO", "M", "MM", "OM", "MOM"
It would be trivial to filter out the empty string and duplicate values if need be, but as it stands it strictly produces all subsets.
I think it is generally better try to avoid generating and eliminating permutations. Text like "aaaaaaaaaaaaaaab" can generate really big amount of duplications.
public static IEnumerable<IEnumerable<T>>
GetPermutationsInner<T>(IEnumerable<IGrouping<T, T>> groupedList, int length)
{
if (length == 1) return groupedList.Select(t => new T[] { t.Key });
return GetPermutationsInner<T>(groupedList, length - 1)
.SelectMany(t => groupedList
.Where(e => t.Count(w => w.Equals(e.Key)) < e.Count())
.Select(s => s.Key),
(t1, t2) => t1.Concat(new T[] { t2 }));
}
public static IEnumerable<IEnumerable<T>>
GetPermutations<T>(IEnumerable<T> list)
{
var resultList = new List<IEnumerable<T>>();
for (int i = 1; i <= list.Count(); ++i)
{
resultList.AddRange(GetPermutationsInner<T>(list.GroupBy(g => g), i));
}
return resultList;
}

using Linq to find all indexes of a matrix satisfying a certain condition

I have this 2 dimensional boolean matrix A
public bool[10][10] A;
Is there a Linq that does what the method below does? (returning all indexes i so that for a given n, A[i][n] is true)
public List<int> getIndex (int n)
{
List<int> resp = new List<int>();
for (int i = 0; i < 10; i++)
if (A[i][n])
{
resp.Add(i);
}
return resp;
}
return Enumerable.Range(0, A.Length).Where(x => A[x][n]).ToList();
should do. Otherwise you can make the whole thing lazy by returning an IEnumerable<int>
public IEnumerable<int> getIndex (int n)
{
return Enumerable.Range(0, A.Length).Where(x => A[x][n]);
}
public List<int> getIndex (int n)
{
return A.Select((x, i) => new { x, i })
.Where(x => x.x[n])
.Select(x => x.i)
.ToList();
}
Should do what you want.

LINQ method to group collection into subgroups with specified number of elements

Does there exist a LINQ method to group a given collection into subgroups with specified number of elements I mean, something like Scala's grouped method.
e.g. in Scala, List(89, 67, 34, 11, 34).grouped(2) gives List(List(89, 67), List(34, 11), List(34)).
In case such a method doesn't exist, what would be the LINQ way to do it?
Yes, you can. But you can argue if it's very pretty...
Int64[] aValues = new Int64[] { 1, 2, 3, 4, 5, 6 };
var result = aValues
.Select( ( x, y ) => new KeyValuePair<Int64, Int32>( x, y ) )
.GroupBy( x => x.Value / 2 )
.Select( x => x.Select( y => y.Key ).ToList() ).ToList();
How it works:
Select x and y from the original collection, where x is the actual value and y is the index of it in the given collection. Then group by integer devision of the index and the desired grouping length ( in this example 2 ).
Grouping by integer devision will round up to the lower - so 0 / 2 = 0, 1 / 2 = 0, etc. which will give us the needed grouping category value. This is what we are grouping against here.
For result select only the values grouped in lists and return them as a collection of lists.
Here is a website that seems to have some sample code to do what you want:
http://www.chinhdo.com/20080515/chunking/
So what you could do is take this method and create an extension method.
Extension method sample:
static class ListExtension
{
public static List<List<T>> BreakIntoChunks<T>(this List<T> list, int chunkSize)
{
if (chunkSize <= 0)
{
throw new ArgumentException("chunkSize must be greater than 0.");
}
List<List<T>> retVal = new List<List<T>>();
while (list.Count > 0)
{
int count = list.Count > chunkSize ? chunkSize : list.Count;
retVal.Add(list.GetRange(0, count));
list.RemoveRange(0, count);
}
return retVal;
}
}
You could try the approach shown in this answer to this similar question.
public static class GroupingExtension
{
public static IEnumerable<IEnumerable<T>> Grouped<T>(
this IEnumerable<T> input,
int groupCount)
{
if (input == null) throw new ArgumentException("input");
if (groupCount < 1) throw new ArgumentException("groupCount");
IEnumerator<T> e = input.GetEnumerator();
while (true)
{
List<T> l = new List<T>();
for (int n = 0; n < groupCount; ++n)
{
if (!e.MoveNext())
{
if (n != 0)
{
yield return l;
}
yield break;
}
l.Add(e.Current);
}
yield return l;
}
}
}
Use like this:
List<int> l = new List<int>{89, 67, 34, 11, 34};
foreach (IEnumerable<int> group in l.Grouped(2)) {
string s = string.Join(", ", group.Select(x => x.ToString()).ToArray());
Console.WriteLine(s);
}
Result:
89, 67
34, 11
34

Find the most occurring number in a List<int>

Is there a quick and nice way using linq?
How about:
var most = list.GroupBy(i=>i).OrderByDescending(grp=>grp.Count())
.Select(grp=>grp.Key).First();
or in query syntax:
var most = (from i in list
group i by i into grp
orderby grp.Count() descending
select grp.Key).First();
Of course, if you will use this repeatedly, you could add an extension method:
public static T MostCommon<T>(this IEnumerable<T> list)
{
return ... // previous code
}
Then you can use:
var most = list.MostCommon();
Not sure about the lambda expressions, but I would
Sort the list [O(n log n)]
Scan the list [O(n)] finding the longest run-length.
Scan it again [O(n)] reporting each number having that run-length.
This is because there could be more than one most-occurring number.
Taken from my answer here:
public static IEnumerable<T> Mode<T>(this IEnumerable<T> input)
{
var dict = input.ToLookup(x => x);
if (dict.Count == 0)
return Enumerable.Empty<T>();
var maxCount = dict.Max(x => x.Count());
return dict.Where(x => x.Count() == maxCount).Select(x => x.Key);
}
var modes = { }.Mode().ToArray(); //returns { }
var modes = { 1, 2, 3 }.Mode().ToArray(); //returns { 1, 2, 3 }
var modes = { 1, 1, 2, 3 }.Mode().ToArray(); //returns { 1 }
var modes = { 1, 2, 3, 1, 2 }.Mode().ToArray(); //returns { 1, 2 }
I went for a performance test between the above approach and David B's TakeWhile.
source = { }, iterations = 1000000
mine - 300 ms, David's - 930 ms
source = { 1 }, iterations = 1000000
mine - 1070 ms, David's - 1560 ms
source = 100+ ints with 2 duplicates, iterations = 10000
mine - 300 ms, David's - 500 ms
source = 10000 random ints with about 100+ duplicates, iterations = 1000
mine - 1280 ms, David's - 1400 ms
Here is another answer, which seems to be fast. I think Nawfal's answer is generally faster but this might shade it on long sequences.
public static IEnumerable<T> Mode<T>(
this IEnumerable<T> source,
IEqualityComparer<T> comparer = null)
{
var counts = source.GroupBy(t => t, comparer)
.Select(g => new { g.Key, Count = g.Count() })
.ToList();
if (counts.Count == 0)
{
return Enumerable.Empty<T>();
}
var maxes = new List<int>(5);
int maxCount = 1;
for (var i = 0; i < counts.Count; i++)
{
if (counts[i].Count < maxCount)
{
continue;
}
if (counts[i].Count > maxCount)
{
maxes.Clear();
maxCount = counts[i].Count;
}
maxes.Add(i);
}
return maxes.Select(i => counts[i].Key);
}
Someone asked for a solution where there's ties. Here's a stab at that:
int indicator = 0
var result =
list.GroupBy(i => i)
.Select(g => new {i = g.Key, count = g.Count()}
.OrderByDescending(x => x.count)
.TakeWhile(x =>
{
if (x.count == indicator || indicator == 0)
{
indicator = x.count;
return true;
}
return false;
})
.Select(x => x.i);
Here's a solution I've written for when there are multiple most common elements.
public static List<T> MostCommonP<T>(this IEnumerable<T> list)
{
return list.GroupBy(element => element)
.GroupBy(group => group.Count())
.MaxBy(groups => groups.Key)
.Select(group => group.Key)
.ToList();
}

Categories