Minimum number of steps using only multiply A by 2, or divide A by 2 or increment A by one to go from number A to B - algorithm

Given two numbers A and B, what is the minimum number of steps to transform number A to become number B.
A step can either be A *= 2, A++ or A /= 2 if and only if A is an even number.
What is the most efficient algorithm to achieve this?
Suppose A and B can be really large numbers.

Here's my take, done in C#.
var a = 2;
var b = 15;
var found = new HashSet<int>() { a };
var operations = new (string operation, Func<int, bool> condition, Func<int, int> projection)[]
{
("/2", x => x % 2 == 0, x => x / 2),
("*2", x => x <= int.MaxValue / 2, x => x *2),
("+1", x => true, x => x + 1),
};
IEnumerable<(int count, string operations, int value)> Project((int count, string operations, int value) current)
{
foreach (var operation in operations)
{
if (operation.condition(current.value))
{
var value = operation.projection(current.value);
if (!found.Contains(value))
{
found.Add(value);
yield return (current.count + 1, $"{current.operations}, {operation.operation}", value);
}
}
}
}
var candidates = new[] { (count: 0, operations: $"{a}", value: a) };
while (!found.Contains(b))
{
candidates =
candidates
.SelectMany(c => Project(c))
.ToArray();
}
var result = candidates.Where(x => x.value == b).First();
Console.WriteLine($"{result.count} operations: {result.operations} = {result.value}");
That outputs:
5 operations: 2, +1, *2, +1, *2, +1 = 15
Basically, this is starting with a at the zeroth step. It then takes this generation and produces all possible values from the operations to create the next generation. If it produces a value that it has already seen it discards the value as there is an equal or faster operation to produce the value. It keeps repeating until b is found.

Related

How to divide a set into two sets such that the difference of the average is minimum?

As I understand, it is related to the partition problem.
But I would like to ask a slightly different problem which I don't care about the sum but the average. In this case, it needs to optimize 2 constraints (sum and number of items) at the same time. It seems to be a harder problem and I cannot see any solutions online.
Are there any solutions for this variant? Or how does it relate to the partition problem?
Example:
input X = [1,1,1,1,1,6]
output based on sum: A = [1,1,1,1,1], B=[6]
output based on average: A = [1], B=[1,1,1,1,6]
On some inputs, a modification of the dynamic program for the usual partition problem will give a speedup. We have to classify each partial solution by its count and sum instead of just sum, which slows things down a bit. Python 3 below (note that the use of dictionaries implicitly collapses functionally identical partial solutions):
def children(ab, x):
a, b = ab
yield a + [x], b
yield a, b + [x]
def proper(ab):
a, b = ab
return a and b
def avg(lst):
return sum(lst) / len(lst)
def abs_diff_avg(ab):
a, b = ab
return abs(avg(a) - avg(b))
def min_abs_diff_avg(lst):
solutions = {(0, 0): ([], [])}
for x in lst:
solutions = {
(sum(a), len(a)): (a, b)
for ab in solutions.values()
for (a, b) in children(ab, x)
}
return min(filter(proper, solutions.values()), key=abs_diff_avg)
print(min_abs_diff_avg([1, 1, 1, 1, 1, 6]))
let S_i the sum of a subset of v of size i
let S be the total sum of v, n the length of v
the err to minimize is
err_i = |avg(S_i) - avg(S-S_i)|
err_i = |S_i/i - (S-S_i)/(n-i)|
err_i = |(nS_i - iS)/(i(n-i))|
algorithm below does:
for all tuple sizes (1,...,n/2) as i
- for all tuples of size i-1 as t_{i-1}
- generate all possible tuple of size i from t_{i-1} by adjoining one elem from v
- track best tuple in regard of err_i
The only cut I found being:
for two tuples of size i having the same sum, keep the one whose last element's index is the smallest
e.g given tuples A, B (where X is some taken element from v)
A: [X,....,X....]
B: [.,X,.....,X..]
keep A because its right-most element has the minimal index
(idea being that at size 3, A will offer the same candidates as B plus some more)
function generateTuples (v, tuples) {
const nextTuples = new Map()
for (const [, t] of tuples) {
for (let l = t.l + 1; l < v.length; ++l) {
const s = t.s + v[l]
if (!nextTuples.has(s) || nextTuples.get(s).l > l) {
const nextTuple = { v: t.v.concat(l), s, l }
nextTuples.set(s, nextTuple)
}
}
}
return nextTuples
}
function processV (v) {
const fErr = (() => {
const n = v.length
const S = v.reduce((s, x) => s + x, 0)
return ({ s: S_i, v }) => {
const i = v.length
return Math.abs((n * S_i - i * S) / (i * (n - i)))
}
})()
let tuples = new Map([[0, { v: [], s: 0, l: -1 }]])
let best = null
let err = 9e3
for (let i = 0; i < Math.ceil(v.length / 2); ++i) {
const nextTuples = generateTuples(v, tuples)
for (const [, t] of nextTuples) {
if (fErr(t) <= err) {
best = t
err = fErr(t)
}
}
tuples = nextTuples
}
const s1Indices = new Set(best.v)
return {
sol: v.reduce(([v1, v2], x, i) => {
(s1Indices.has(i) ? v1 : v2).push(x)
return [v1, v2]
}, [[], []]),
err
}
}
console.log('best: ', processV([1, 1, 1, 1, 1, 6]))
console.log('best: ', processV([1, 2, 3, 4, 5]))
console.log('best: ', processV([1, 3, 5, 7, 7, 8]))

Single Number 2 Scala solution

Given an array of integers, every element appears three times except for one, which appears exactly once. Find that single one. This is what I have now. But I don't know how to break the for loop once I get the single number "b". Any solution in scala please?
for(Array(a,b) <- nums.sorted.sliding(2))
{
if (a == b){j = j+1}
else
{
if (j < 3) j =1
b
}
}
This will do it.
nums.groupBy(identity).find(_._2.length == 1).get._1
It's a bit unsafe in that it will throw if there is no single-count element. It can be made safer if a default value is returned when no single-count element is found.
nums.groupBy(identity).find(_._2.length == 1).fold(-1)(_._1)
Another way is to sum the array by adding digits of two numbers in base 3 modulo 3 (in other words XOR in base 3). The elements that appear 3 times will become zero, so the result of this sum will be the single number.
def findSingleNumber(numbers: Array[Int]) = {
def add3(a: String, b: String): String = a.zipAll(b, '0', '0').map {
case (i, j) => ((i.toInt + j.toInt) % 3 + '0').toChar
}(collection.breakOut)
val numbersInBase3 = numbers.map(n => Integer.toString(n, 3).reverse)
Integer.parseInt(numbersInBase3.fold("0")(add3).reverse, 3)
}
scala> findSingleNumber(Array(10, 20, 30, 100, 20, 100, 10, 10, 20, 100))
res1: Int = 30
Or representing base 3 numbers as digit arrays:
def findSingleNumber(numbers: Array[Int]) = {
def toBase3(int: Int): Array[Int] =
Iterator.iterate(int)(_ / 3).takeWhile(_ != 0).map(_ % 3).toArray
def toBase10(arr: Array[Int]): Int =
arr.reverseIterator.foldLeft(0)(_ * 3 + _)
def add3(a: Array[Int], b: Array[Int]): Array[Int] = a.zipAll(b, 0, 0).map {
case (i, j) => (i + j) % 3
}
toBase10(numbers.map(toBase3).fold(Array.empty[Int])(add3))
}

Split a random value into four that sum up to it

I have one value like 24, and I have four textboxes. How can I dynamically generate four values that add up to 24?
All the values must be integers and can't be negative, and the result cannot be 6, 6, 6, 6; they must be different like: 8, 2, 10, 4. (But 5, 6, 6, 7 would be okay.)
For your stated problem, it is possible to generate an array of all possible solutions and then pick one randomly. There are in fact 1,770 possible solutions.
var solutions = [[Int]]()
for i in 1...21 {
for j in 1...21 {
for k in 1...21 {
let l = 24 - (i + j + k)
if l > 0 && !(i == 6 && j == 6 && k == 6) {
solutions.append([i, j, k, l])
}
}
}
}
// Now generate 20 solutions
for _ in 1...20 {
let rval = Int(arc4random_uniform(UInt32(solutions.count)))
println(solutions[rval])
}
This avoids any bias at the cost of initial setup time and storage.
This could be improved by:
Reducing storage space by only storing the first 3 numbers. The 4th one is always 24 - (sum of first 3)
Reducing storage space by storing each solution as a single integer: (i * 10000 + j * 100 + k)
Speeding up the generation of solutions by realizing that each loop doesn't need to go to 21.
Here is the solution that stores each solution as a single integer and optimizes the loops:
var solutions = [Int]()
for i in 1...21 {
for j in 1...22-i {
for k in 1...23-i-j {
if !(i == 6 && j == 6 && k == 6) {
solutions.append(i * 10000 + j * 100 + k)
}
}
}
}
// Now generate 20 solutions
for _ in 1...20 {
let rval = Int(arc4random_uniform(UInt32(solutions.count)))
let solution = solutions[rval]
// unpack the values
let i = solution / 10000
let j = (solution % 10000) / 100
let k = solution % 100
let l = 24 - (i + j + k)
// print the solution
println("\([i, j, k, l])")
}
Here is a Swift implementation of the algorithm given in https://stackoverflow.com/a/8064754/1187415, with a slight
modification because all numbers are required to be positive.
The method to producing N positive random integers with sum M is
Build an array containing the number 0, followed by N-1 different
random numbers in the range 1 .. M-1, and finally the number M.
Compute the differences of subsequent array elements.
In the first step, we need a random subset of N-1 elements out of
the set { 1, ..., M-1 }. This can be achieved by iterating over this
set and choosing each element with probability n/m, where
m is the remaining number of elements we can choose from and
n is the remaining number of elements to choose.
Instead of storing the chosen random numbers in an array, the
difference to the previously chosen number is computed immediately
and stored.
This gives the following function:
func randomNumbers(#count : Int, withSum sum : Int) -> [Int] {
precondition(sum >= count, "`sum` must not be less than `count`")
var diffs : [Int] = []
var last = 0 // last number chosen
var m = UInt32(sum - 1) // remaining # of elements to choose from
var n = UInt32(count - 1) // remaining # of elements to choose
for i in 1 ..< sum {
// Choose this number `i` with probability n/m:
if arc4random_uniform(m) < n {
diffs.append(i - last)
last = i
n--
}
m--
}
diffs.append(sum - last)
return diffs
}
println(randomNumbers(count: 4, withSum: 24))
If a solution with all elements equal (e.g 6+6+6+6=24) is not
allowed, you can repeat the method until a valid solution is found:
func differentRandomNumbers(#count : Int, withSum sum : Int) -> [Int] {
precondition(count >= 2, "`count` must be at least 2")
var v : [Int]
do {
v = randomNumbers(count: count, withSum: sum)
} while (!contains(v, { $0 != v[0]} ))
return v
}
Here is a simple test. It computes 1,000,000 random representations
of 7 as the sum of 3 positive integers, and counts the distribution
of the results.
let set = NSCountedSet()
for i in 1 ... 1_000_000 {
let v = randomNumbers(count: 3, withSum: 7)
set.addObject(v)
}
for (_, v) in enumerate(set) {
let count = set.countForObject(v)
println("\(v as! [Int]) \(count)")
}
Result:
[1, 4, 2] 66786
[1, 5, 1] 67082
[3, 1, 3] 66273
[2, 2, 3] 66808
[2, 3, 2] 66966
[5, 1, 1] 66545
[2, 1, 4] 66381
[1, 3, 3] 67153
[3, 3, 1] 67034
[4, 1, 2] 66423
[3, 2, 2] 66674
[2, 4, 1] 66418
[4, 2, 1] 66292
[1, 1, 5] 66414
[1, 2, 4] 66751
Update for Swift 3:
func randomNumbers(count : Int, withSum sum : Int) -> [Int] {
precondition(sum >= count, "`sum` must not be less than `count`")
var diffs : [Int] = []
var last = 0 // last number chosen
var m = UInt32(sum - 1) // remaining # of elements to choose from
var n = UInt32(count - 1) // remaining # of elements to choose
for i in 1 ..< sum {
// Choose this number `i` with probability n/m:
if arc4random_uniform(m) < n {
diffs.append(i - last)
last = i
n -= 1
}
m -= 1
}
diffs.append(sum - last)
return diffs
}
print(randomNumbers(count: 4, withSum: 24))
Update for Swift 4.2 (and later), using the unified random API:
func randomNumbers(count : Int, withSum sum : Int) -> [Int] {
precondition(sum >= count, "`sum` must not be less than `count`")
var diffs : [Int] = []
var last = 0 // last number chosen
var m = sum - 1 // remaining # of elements to choose from
var n = count - 1 // remaining # of elements to choose
for i in 1 ..< sum {
// Choose this number `i` with probability n/m:
if Int.random(in: 0..<m) < n {
diffs.append(i - last)
last = i
n -= 1
}
m -= 1
}
diffs.append(sum - last)
return diffs
}
func getRandomValues(amountOfValues:Int, totalAmount:Int) -> [Int]?{
if amountOfValues < 1{
return nil
}
if totalAmount < 1{
return nil
}
if totalAmount < amountOfValues{
return nil
}
var values:[Int] = []
var valueLeft = totalAmount
for i in 0..<amountOfValues{
if i == amountOfValues - 1{
values.append(valueLeft)
break
}
var value = Int(arc4random_uniform(UInt32(valueLeft - (amountOfValues - i))) + 1)
valueLeft -= value
values.append(value)
}
var shuffledArray:[Int] = []
for i in 0..<values.count {
var rnd = Int(arc4random_uniform(UInt32(values.count)))
shuffledArray.append(values[rnd])
values.removeAtIndex(rnd)
}
return shuffledArray
}
getRandomValues(4, 24)
This is not a final answer, but it should be a (good) starting point.
How it works: It takes 2 parameters. The amount of random values (4 in your case) and the total amount (24 in your case).
It takes a random value between the total Amount and 0, stores this in an array and it subtracts this from a variable which stores the amount that is left and stores the new value.
Than it takes a new random value between the amount that is left and 0, stores this in an array and it again subtracts this from the amount that is left and stores the new value.
When it is the last number needed, it sees what amount is left and adds that to the array
EDIT:
Adding a +1 to the random value removes the problem of having 0 in your array.
EDIT 2:
Shuffling the array does remove the increased chance of having a high value as the first value.
One solution that is unfortunatly non-deterministic but completely random is as follows:
For a total of 24 in 4 numbers:
pick four random numbers between 1 and 21
repeat until the total of the numbers equals 24 and they are not all 6.
This will, on average, loop about 100 times before finding a solution.
Here's a solution which should have significantly* less bias than some of the other methods. It works by generating the requested number of random floating point numbers, multiplying or dividing all of them until they add up to the target total, and then rounding them into integers. The rounding process changes the total, so we need to correct for that by adding or subtracting from random terms until they add up to the right amount.
func getRandomDoubles(#count: Int, #total: Double) -> [Double] {
var nonNormalized = [Double]()
nonNormalized.reserveCapacity(count)
for i in 0..<count {
nonNormalized.append(Double(arc4random()) / 0xFFFFFFFF)
}
let nonNormalizedSum = reduce(nonNormalized, 0) { $0 + $1 }
let normalized = nonNormalized.map { $0 * total / nonNormalizedSum }
return normalized
}
func getRandomInts(#count: Int, #total: Int) -> [Int] {
let doubles = getRandomDoubles(count: count, total: Double(total))
var ints = [Int]()
ints.reserveCapacity(count)
for double in doubles {
if double < 1 || double % 1 >= 0.5 {
// round up
ints.append(Int(ceil(double)))
} else {
// round down
ints.append(Int(floor(double)))
}
}
let roundingErrors = total - (reduce(ints, 0) { $0 + $1 })
let directionToAdjust: Int = roundingErrors > 0 ? 1 : -1
var corrections = abs(roundingErrors)
while corrections > 0 {
let index = Int(arc4random_uniform(UInt32(count)))
if directionToAdjust == -1 && ints[index] <= 1 { continue }
ints[index] += directionToAdjust
corrections--
}
return ints
}
*EDIT: Martin R has correctly pointed out that this is not nearly as uniform as one might expect, and is in fact highly biased towards numbers in the middle of the 1-24 range. I would not recommend using this solution, but I'm leaving it up so that others can know not to make the same mistake.
As a recursive function the algorithm is very nice:
func getRandomValues(amount: Int, total: Int) -> [Int] {
if amount == 1 { return [total] }
if amount == total { return Array(count: amount, repeatedValue: 1) }
let number = Int(arc4random()) % (total - amount + 1) + 1
return [number] + getRandomValues(amount - 1, total - number)
}
And with safety check:
func getRandomValues(amount: Int, total: Int) -> [Int]? {
if !(1...total ~= amount) { return nil }
if amount == 1 { return [total] }
if amount == total { return Array(count: amount, repeatedValue: 1) }
let number = Int(arc4random()) % (total - amount + 1) + 1
return [number] + getRandomValues(amount - 1, total - number)!
}
As #MartinR pointed out the code above is extremely biased. So in order to have a uniform distribution of the output values you should use this piece of code:
func getRandomValues(amount: Int, total: Int) -> [Int] {
var numberSet = Set<Int>()
// add splitting points to numberSet
for _ in 1...amount - 1 {
var number = Int(arc4random()) % (total - 1) + 1
while numberSet.contains(number) {
number = Int(arc4random()) % (total - 1) + 1
}
numberSet.insert(number)
}
// sort numberSet and return the differences between the splitting points
let sortedArray = (Array(numberSet) + [0, total]).sort()
return sortedArray.enumerate().flatMap{
indexElement in
if indexElement.index == amount { return nil }
return sortedArray[indexElement.index + 1] - indexElement.element
}
}
A javascript implementation for those who may be looking for such case:
const numbersSumTo = (length, value) => {
const fourRandomNumbers = Array.from({ length: length }, () => Math.floor(Math.random() * 6) + 1);
const res = fourRandomNumbers.map(num => (num / fourRandomNumbers.reduce((a, b) => a + b, 0)) * value).map(num => Math.trunc(num));
res[0] += Math.abs(res.reduce((a, b) => a + b, 0) - value);
return res;
}
// Gets an array with 4 items which sum to 100
const res = numbersSumTo(4, 100);
const resSum = res.reduce((a, b) => a + b, 0);
console.log({
res,
resSum
});
Also plenty of different methods of approach can be found here on this question: https://math.stackexchange.com/questions/1276206/method-of-generating-random-numbers-that-sum-to-100-is-this-truly-random

Algorithm to generate a sequence proportional to specified percentage

Given a Map of objects and designated proportions (let's say they add up to 100 to make it easy):
val ss : Map[String,Double] = Map("A"->42, "B"->32, "C"->26)
How can I generate a sequence such that for a subset of size n there are ~42% "A"s, ~32% "B"s and ~26% "C"s? (Obviously, small n will have larger errors).
(Work language is Scala, but I'm just asking for the algorithm.)
UPDATE: I resisted a random approach since, for instance, there's ~16% chance that the sequence would start with AA and ~11% chance it would start with BB and there would be very low odds that for n precisely == (sum of proportions) the distribution would be perfect. So, following #MvG's answer, I implemented as follows:
/**
Returns the key whose achieved proportions are most below desired proportions
*/
def next[T](proportions : Map[T, Double], achievedToDate : Map[T,Double]) : T = {
val proportionsSum = proportions.values.sum
val desiredPercentages = proportions.mapValues(v => v / proportionsSum)
//Initially no achieved percentages, so avoid / 0
val toDateTotal = if(achievedToDate.values.sum == 0.0){
1
}else{
achievedToDate.values.sum
}
val achievedPercentages = achievedToDate.mapValues(v => v / toDateTotal)
val gaps = achievedPercentages.map{ case (k, v) =>
val gap = desiredPercentages(k) - v
(k -> gap)
}
val maxUnder = gaps.values.toList.sortWith(_ > _).head
//println("Max gap is " + maxUnder)
val gapsForMaxUnder = gaps.mapValues{v => Math.abs(v - maxUnder) < Double.Epsilon }
val keysByHasMaxUnder = gapsForMaxUnder.map(_.swap)
keysByHasMaxUnder(true)
}
/**
Stream of most-fair next element
*/
def proportionalStream[T](proportions : Map[T, Double], toDate : Map[T, Double]) : Stream[T] = {
val nextS = next(proportions, toDate)
val tailToDate = toDate + (nextS -> (toDate(nextS) + 1.0))
Stream.cons(
nextS,
proportionalStream(proportions, tailToDate)
)
}
That when used, e.g., :
val ss : Map[String,Double] = Map("A"->42, "B"->32, "C"->26)
val none : Map[String,Double] = ss.mapValues(_ => 0.0)
val mySequence = (proportionalStream(ss, none) take 100).toList
println("Desired : " + ss)
println("Achieved : " + mySequence.groupBy(identity).mapValues(_.size))
mySequence.map(s => print(s))
println
produces :
Desired : Map(A -> 42.0, B -> 32.0, C -> 26.0)
Achieved : Map(C -> 26, A -> 42, B -> 32)
ABCABCABACBACABACBABACABCABACBACABABCABACABCABACBA
CABABCABACBACABACBABACABCABACBACABABCABACABCABACBA
For a deterministic approach, the most obvious solution would probably be this:
Keep track of the number of occurrences of each item in the sequence so far.
For the next item, choose that item for which the difference between intended and actual count (or proportion, if you prefer that) is maximal, but only if the intended count (resp. proportion) is greater than the actual one.
If there is a tie, break it in an arbitrary but deterministic way, e.g. choosing the alphabetically lowest item.
This approach would ensure an optimal adherence to the prescribed ratio for every prefix of the infinite sequence generated in this way.
Quick & dirty python proof of concept (don't expect any of the variable “names” to make any sense):
import sys
p = [0.42, 0.32, 0.26]
c = [0, 0, 0]
a = ['A', 'B', 'C']
n = 0
while n < 70*5:
n += 1
x = 0
s = n*p[0] - c[0]
for i in [1, 2]:
si = n*p[i] - c[i]
if si > s:
x = i
s = si
sys.stdout.write(a[x])
if n % 70 == 0:
sys.stdout.write('\n')
c[x] += 1
Generates
ABCABCABACABACBABCAABCABACBACABACBABCABACABACBACBAABCABCABACABACBABCAB
ACABACBACABACBABCABACABACBACBAABCABCABACABACBABCAABCABACBACABACBABCABA
CABACBACBAABCABCABACABACBABCABACABACBACBAACBABCABACABACBACBAABCABCABAC
ABACBABCABACABACBACBAACBABCABACABACBACBAABCABCABACABACBABCABACABACBACB
AACBABCABACABACBACBAABCABCABACABACBABCAABCABACBACBAACBABCABACABACBACBA
For every item of the sequence, compute a (pseudo-)random number r equidistributed between 0 (inclusive) and 100 (exclusive).
If 0 ≤ r < 42, take A
If 42 ≤ r < (42+32), take B
If (42+32) ≤ r < (42+32+26)=100, take C
The number of each entry in your subset is going to be the same as in your map, but with a scaling factor applied.
The scaling factor is n/100.
So if n was 50, you would have { Ax21, Bx16, Cx13 }.
Randomize the order to your liking.
The simplest "deterministic" [in terms of #elements of each category] solution [IMO] will be: add elements in predefined order, and then shuffle the resulting list.
First, add map(x)/100 * n elements from each element x chose how you handle integer arithmetics to avoid off by one element], and then shuffle the resulting list.
Shuffling a list is simple with fisher-yates shuffle, which is implemented in most languages: for example java has Collections.shuffle(), and C++ has random_shuffle()
In java, it will be as simple as:
int N = 107;
List<String> res = new ArrayList<String>();
for (Entry<String,Integer> e : map.entrySet()) { //map is predefined Map<String,Integer> for frequencies
for (int i = 0; i < Math.round(e.getValue()/100.0 * N); i++) {
res.add(e.getKey());
}
}
Collections.shuffle(res);
This is nondeterministic, but gives a distribution of values close to MvG's. It suffers from the problem that it could give AAA right at the start. I post it here for completeness' sake given how it proves my dissent with MvG was misplaced (and I don't expect any upvotes).
Now, if someone has an idea for an expand function that is deterministic and won't just duplicate MvG's method (rendering the calc function useless), I'm all ears!
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>ErikE's answer</title>
</head>
<body>
<div id="output"></div>
<script type="text/javascript">
if (!Array.each) {
Array.prototype.each = function(callback) {
var i, l = this.length;
for (i = 0; i < l; i += 1) {
callback(i, this[i]);
}
};
}
if (!Array.prototype.sum) {
Array.prototype.sum = function() {
var sum = 0;
this.each(function(i, val) {
sum += val;
});
return sum;
};
}
function expand(counts) {
var
result = "",
charlist = [],
l,
index;
counts.each(function(i, val) {
char = String.fromCharCode(i + 65);
for ( ; val > 0; val -= 1) {
charlist.push(char);
}
});
l = charlist.length;
for ( ; l > 0; l -= 1) {
index = Math.floor(Math.random() * l);
result += charlist[index];
charlist.splice(index, 1);
}
return result;
}
function calc(n, proportions) {
var percents = [],
counts = [],
errors = [],
fnmap = [],
errorSum,
worstIndex;
fnmap[1] = "min";
fnmap[-1] = "max";
proportions.each(function(i, val) {
percents[i] = val / proportions.sum() * n;
counts[i] = Math.round(percents[i]);
errors[i] = counts[i] - percents[i];
});
errorSum = counts.sum() - n;
while (errorSum != 0) {
adjust = errorSum < 0 ? 1 : -1;
worstIndex = errors.indexOf(Math[fnmap[adjust]].apply(0, errors));
counts[worstIndex] += adjust;
errors[worstIndex] = counts[worstIndex] - percents[worstIndex];
errorSum += adjust;
}
return expand(counts);
}
document.body.onload = function() {
document.getElementById('output').innerHTML = calc(99, [25.1, 24.9, 25.9, 24.1]);
};
</script>
</body>
</html>

LINQ: GroupBy with maximum count in each group

I have a list of duplicate numbers:
Enumerable.Range(1,3).Select(o => Enumerable.Repeat(o, 3)).SelectMany(o => o)
// {1,1,1,2,2,2,3,3,3}
I group them and get quantity of occurance:
Enumerable.Range(1,3).Select(o => Enumerable.Repeat(o, 3)).SelectMany(o => o)
.GroupBy(o => o).Select(o => new { Qty = o.Count(), Num = o.Key })
Qty Num
3 1
3 2
3 3
What I really need is to limit the quantity per group to some number. If the limit is 2 the result for the above grouping would be:
Qty Num
2 1
1 1
2 2
1 2
2 3
1 3
So, if Qty = 10 and limit is 4, the result is 3 rows (4, 4, 2). The Qty of each number is not equal like in example. The specified Qty limit is the same for whole list (doesn't differ based on number).
Thanks
Some of the other answers are making the LINQ query far more complex than it needs to be. Using a foreach loop is certainly faster and more efficient, but the LINQ alternative is still fairly straightforward.
var input = Enumerable.Range(1, 3).SelectMany(x => Enumerable.Repeat(x, 10));
int limit = 4;
var query =
input.GroupBy(x => x)
.SelectMany(g => g.Select((x, i) => new { Val = x, Grp = i / limit }))
.GroupBy(x => x, x => x.Val)
.Select(g => new { Qty = g.Count(), Num = g.Key.Val });
There was a similar question that came up recently asking how to do this in SQL - there's no really elegant solution and unless this is Linq to SQL or Entity Framework (i.e. being translated into a SQL query), I'd really suggest that you not try to solve this problem with Linq and instead write an iterative solution; it's going to be a great deal more efficient and easier to maintain.
That said, if you absolutely must use a set-based ("Linq") method, this is one way you could do it:
var grouped =
from n in nums
group n by n into g
select new { Num = g.Key, Qty = g.Count() };
int maxPerGroup = 2;
var portioned =
from x in grouped
from i in Enumerable.Range(1, grouped.Max(g => g.Qty))
where (x.Qty % maxPerGroup) == (i % maxPerGroup)
let tempQty = (x.Qty / maxPerGroup) == (i / maxPerGroup) ?
(x.Qty % maxPerGroup) : maxPerGroup
select new
{
Num = x.Num,
Qty = (tempQty > 0) ? tempQty : maxPerGroup
};
Compare with the simpler and faster iterative version:
foreach (var g in grouped)
{
int remaining = g.Qty;
while (remaining > 0)
{
int allotted = Math.Min(remaining, maxPerGroup);
yield return new MyGroup(g.Num, allotted);
remaining -= allotted;
}
}
Aaronaught's excellent answer doesn't cover the possibility of getting the best of both worlds... using an extension method to provide an iterative solution.
Untested:
public static IEnumerable<IEnumerable<U>> SplitByMax<T, U>(
this IEnumerable<T> source,
int max,
Func<T, int> maxSelector,
Func<T, int, U> resultSelector
)
{
foreach(T x in source)
{
int number = maxSelector(x);
List<U> result = new List<U>();
do
{
int allotted = Math.Min(number, max);
result.Add(resultSelector(x, allotted));
number -= allotted
} while (number > 0 && max > 0);
yield return result;
}
}
Called by:
var query = grouped.SplitByMax(
10,
o => o.Qty,
(o, i) => new {Num = o.Num, Qty = i}
)
.SelectMany(split => split);

Resources