Related
Given the first 9 natural numbers we have to assign + or - sign to each number such that the sum of the resulting sequence is equal to the required number.
For example, we are given the first 9 natural numbers {1,2,3,4,5,6,7,8,9} and have to get the value 1.
The output can be {1,-2,3,4,-5,-6,7,8,-9} or any one of the other possibilities.
if the value is -5, then the output can be {-1,-2,-3,4,-5,-6,7,-8,9}.
We know that the sum will always be odd.
I am unable to come up with an algorithm that could solve this problem.
My idea was to use a greedy approach.(we can assume the sequence is sorted)
Start from the largest value (right side of the sequence).
Sum the numbers till current index.
Check if the sum of sequence is lower than absolute value of the target number.
If yes, then assign the + to the number at the current index otherwise a -.
Repeat till we reach the first number.
Based on my algorithm the output for,
1 is {1 -2 3 4 -5 6 -7 8 -9}
15 is {1 2 -3 4 -5 6 7 -8 9}. Giving me the sum of the sequence as 13 (completely wrong).
Here is my code,
public static int[] sequenceOfSigns(int[] num, int sum) {
int n = num[num.length - 1];
int upperBound = (n * (n + 1)) / 2;
if (sum % 2 == 0 || sum < -upperBound || sum > upperBound)
return null;
int tempSum = 0;
for (int i = num.length - 1; i >= 0; i--) {
tempSum += num[i];
boolean flag;
if (i != 0)
flag = tempSum >= Math.abs(sum);
else
flag = tempSum > Math.abs(sum);
if (flag) {
num[i] = -num[i];
tempSum += num[i] * 2;
}
}
return num;
}
Any ideas on how to modify the algorithm would be greatly appreciated.
Additionally can we extend the problem to n natural numbers? or is it an NP-problem?
EDIT
I have solved this problem another way.
Here is the solution with proof that was inspired by my brother's suggestion on how to solve the problem. His suggestion is superbly elegant.
My Proof
Given a sequence of natural numbers {1,2,3,4,5,6,7,8,9} and an odd target sum of x; we are to modify the sign(+/-) of the numbers in the sequence such that the sum of the elements equals the target number.
Let A = {1,2,3,4,5,6,7,8,9}
x is the target number (odd).
Let us consider a set S which contains all the elements of A that should be made negative.
Then we have the relation,
Sum of all elements in A - (2 * Sum of all elements in S) = |x|
[This is the key idea suggested by my brother]
Rearranging the equation we get,
Sum of all elements in S = (Sum of all elements in A - |x|)/2 .... (1)
Now, we select the largest value in A which is lesser than or equal to the sum in (1).
We subtract this from the sum in (1).
We perform this operation, iteratively, till the sum in (1) becomes 0.
A point to note is that each value from A can only be selected once.
This is our greedy algorithm.
This choice of largest element in A which is lesser than or equal to the sum at each step is a safe greedy choice. Since, the selected values will form the set of numbers which should be negative the sum of the selected values and the sum in (1) should be same.
Now, if we select a value greater than the sum in (1) the difference would be a negative value and that is a contradiction. Hence, we cannot select a greater value.
If we arbitrarily choose any value that is lesser than or equal to the sum in (1), the difference is positive or 0. We can easily replace the selected value with the largest value that satisfies the conditions and the constraints are still obeyed. So, our choice is a safe choice.
The values selected in the previous step form the set S.
and our required sequence is the ordered set formed from the set,
(A - S) U -(elements in S)
If x is negative we simply flip the signs in the ordered set obtained above. Since, the solutions for x and -x are symmetric.
This concludes our proof and solution for the greedy algorithm
CODE
public static int[] sequenceOfSigns(int[] num, int sum) {
int n = num[num.length - 1];
int upperBound = (n * (n + 1)) / 2;
if (sum % 2 == 0 || sum < -upperBound || sum > upperBound)
return null;
int negativeSeqSum = (upperBound - Math.abs(sum)) / 2;
for (int i = num.length - 1; i >= 0; i--) {
if (num[i] <= negativeSeqSum) {
negativeSeqSum -= num[i];
num[i] = -num[i];
}
if (negativeSeqSum == 0)
break;
}
if (sum == -Math.abs(sum))
for (int i = 0; i < num.length; i++)
num[i] = -num[i];
return num;
}
You could solve this by using the following intuition:
Any number could get a + or - sign.
Lets say the last number is 'n' and you could mark it positive or negative
If you mark it positive:
Then remaining sum is S - n and you have to solve the problem for n-1 numbers and target sum = S - n
If you mark it negative:
Then remaining sum is S + n and you have to solve the problem for n-1 numbers and target sum = S + n
Therefore you could write the following recursion:
isPossible(N, S) = isPossible(N-1, S-N) || isPossible(N-1, S+N)
You could memoize the solution at each combination of (N, S) and store the result in a map of
Pair ( N, S) => Boolean
Now from the map you could go through the pairs which have true values i.e. ( for the pair's 'N' numbers it is possible to get sum equal to the pair's S) and get the appropriate signs like below example :
The map for the case N = 9 and S = -5 looks like:
{(1, -31)=false, (2, -33)=false, (6, -29)=false, (2, -37)=false, (5, -35)=false, (4, -4)=true, (2, -41)=false, (3, -10)=false, (3, -14)=false, (4, -16)=false, (3, -18)=false, (3, -22)=false, (3, -26)=false, (7, -22)=true, (4, -28)=false, (3, -30)=false, (1, -1)=true, (3, -34)=false, (2, -3)=true, (1, -5)=false, (2, -7)=false, (1, -9)=false, (4, -40)=false, (1, -49)=false, (2, -11)=false, (1, -13)=false, (5, -9)=true, (9, -5)=true, (1, -45)=false, (2, -15)=false, (1, -17)=false, (1, -41)=false, (2, -19)=false, (6, -15)=true, (1, -21)=false, (1, -37)=false, (2, -23)=false, (1, -25)=false, (5, -21)=false, (1, -33)=false, (2, -27)=false, (1, -29)=false, (2, -31)=false, (3, 0)=true, (2, -35)=false, (2, -39)=false, (3, -8)=false, (3, -12)=false, (2, -47)=false, (4, -14)=false, (4, -18)=false, (8, -14)=true, (3, -20)=false, (3, -24)=false, (4, -26)=false, (4, -30)=false, (3, -32)=false, (1, -3)=false, (3, -36)=false, (2, -5)=false, (1, -7)=false, (2, -9)=false, (1, -11)=false, (3, -44)=false, (2, -13)=false, (1, -15)=false, (1, -43)=false, (2, -17)=false, (1, -19)=false, (1, -39)=false, (2, -21)=false, (1, -23)=false, (1, -35)=false, (2, -25)=false, (1, -27)=false, (5, -23)=false, (2, -29)=false}
Start with the key = Pair(9, -5) is it there in the map with a value of "true"?
Yes it is there, now look for two possibilities : Pair(8, -5-9) or Pair(8, -5+9)
It turns out that Pair(8, -14) exists, so you need to add +9 to the list because you need to look for S - n i.e. you are subtracting n from the remaining S you are looking for. Then update S = S- n = -5 - 9 = -14.
Now look for two possiblities : Pair(7, -14-8) or Pair(7, -14+8). It turns out that Pair(7, -22) exists, so you need to add +8 to the list because you need to look for S - n which means you subtracted n = 8 from the remaining S you are looking for. Then update S = S - n = -14 - 8 = -22
....
....
The final result will be [-1, -2, 3, -4, -5, -6, -7, 8, 9]
Code is - ( you can play with the code here by modifying N, S)
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
class Main {
private static Map<Pair, Boolean> map = new HashMap<>();
public static void main(String... args) {
int N = 9;
int S = -5;
Integer[] result = sequenceOfSigns(N, S);
System.out.println(("Input = " + N + ", " + S + " : ") + ( result == null ? "Not possible" : Arrays.toString(result)));
S = 15;
result = sequenceOfSigns(N, S);
System.out.println(("Input = " + N + ", " + S + " : ") + ( result == null ? "Not possible" : Arrays.toString(result)));
S = 45;
result = sequenceOfSigns(N, S);
System.out.println(("Input = " + N + ", " + S + " : ") + ( result == null ? "Not possible" : Arrays.toString(result)));
S = -45;
result = sequenceOfSigns(N, S);
System.out.println(("Input = " + N + ", " + S + " : ") + ( result == null ? "Not possible" : Arrays.toString(result)));
S = 4;
result = sequenceOfSigns(N, S);
System.out.println(("Input = " + N + ", " + S + " : ") + ( result == null ? "Not possible" : Arrays.toString(result)));
}
public static Integer[] sequenceOfSigns(int N, int sum) {
boolean isPossible = helper(N, sum);
if ( isPossible ) {
int s = sum;
List<Integer> res_list = new ArrayList<>();
for ( int n = N; n >= 1; n-- ) {
Pair p = new Pair(n, s);
if ( n == 1 ) {
res_list.add(s);
} else if ( map.containsKey(p) && map.get(p) ) {
Pair p1 = new Pair(n-1, s+n);
Pair p2 = new Pair(n-1, s-n);
if (map.containsKey(p1)) {
res_list.add(-n);
s = s + n;
} else if ( map.containsKey(p2)) {
res_list.add(n);
s = s - n;
}
}
}
Collections.reverse(res_list);
if ( !res_list.isEmpty() ) return res_list.toArray(new Integer[] {});
}
return null;
}
private static boolean helper(int N, int S) {
if ( N == 0 ) return S == 0;
Pair p = new Pair(N, S);
if ( map.containsKey(p) ) {
return map.get(p);
}
boolean val = helper(N-1, S - N) || helper(N-1, S + N);
map.put(p, val);
return val;
}
static class Pair {
int N, S;
public Pair(int N, int S) {
this.N = N;
this.S = S;
}
#Override
public boolean equals(Object obj) {
if ( obj instanceof Pair ) {
Pair other = (Pair)obj;
return other.N == N && other.S == S;
}
return false;
}
#Override
public int hashCode() {
return 31*31*N + 31*S;
}
#Override
public String toString() {
return "(" + N + ", " + S + ")";
}
}
}
Output:
Input = 9, -5 : [-1, -2, 3, -4, -5, -6, -7, 8, 9]
Input = 9, 15 : [-1, -2, -3, -4, -5, 6, 7, 8, 9]
Input = 9, 45 : [1, 2, 3, 4, 5, 6, 7, 8, 9]
Input = 9, -45 : [-1, -2, -3, -4, -5, -6, -7, -8, -9]
Input = 9, 4 : Not possible
There are 2^9 = 512 possibilities. Why not brute force?
var hash = {0: [[]]};
for (let i=1; i<=9; i++){
let new_hash = {};
for (let _sum in hash){
let sum = Number(_sum);
let new_sum = sum + i;
if (new_sum in new_hash)
new_hash[new_sum] = new_hash[new_sum].concat(hash[sum].map(x => x.concat(i)));
else
new_hash[new_sum] = hash[sum].map(x => x.concat(i));
new_sum = sum - i;
if (new_sum in new_hash)
new_hash[new_sum] = new_hash[new_sum].concat(hash[sum].map(x => x.concat(-i)));
else
new_hash[new_sum] = hash[sum].map(x => x.concat(-i));
}
hash = new_hash;
}
console.log(JSON.stringify(hash[1]));
console.log('');
console.log(JSON.stringify(hash[15]));
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))
}
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
I came across another codechef problem which I am attempting to solve in Scala. The problem statement is as follows:
Stepford Street was a dead end street. The houses on Stepford Street
were bought by wealthy millionaires. They had them extensively altered
so that as one progressed along the street, the height of the
buildings increased rapidly. However, not all millionaires were
created equal. Some refused to follow this trend and kept their houses
at their original heights. The resulting progression of heights was
thus disturbed. A contest to locate the most ordered street was
announced by the Beverly Hills Municipal Corporation. The criteria for
the most ordered street was set as follows: If there exists a house
with a lower height later in the street than the house under
consideration, then the pair (current house, later house) counts as 1
point towards the disorderliness index of the street. It is not
necessary that the later house be adjacent to the current house. Note:
No two houses on a street will be of the same height For example, for
the input: 1 2 4 5 3 6 The pairs (4,3), (5,3) form disordered pairs.
Thus the disorderliness index of this array is 2. As the criteria for
determining the disorderliness is complex, the BHMC has requested your
help to automate the process. You need to write an efficient program
that calculates the disorderliness index of a street.
A sample input output provided is as follows:
Input: 1 2 4 5 3 6
Output: 2
The output is 2 because of two pairs (4,3) and (5,3)
To solve this problem I thought I should use a variant of MergeSort,incrementing by 1 when the left element is greater than the right element.
My scala code is as follows:
def dysfunctionCalc(input:List[Int]):Int = {
val leftHalf = input.size/2
println("HalfSize:"+leftHalf)
val isOdd = input.size%2
println("Is odd:"+isOdd)
val leftList = input.take(leftHalf+isOdd)
println("LeftList:"+leftList)
val rightList = input.drop(leftHalf+isOdd)
println("RightList:"+rightList)
if ((leftList.size <= 1) && (rightList.size <= 1)){
println("Entering input where both lists are <= 1")
if(leftList.size == 0 || rightList.size == 0){
println("One of the lists is less than 0")
0
}
else if(leftList.head > rightList.head)1 else 0
}
else{
println("Both lists are greater than 1")
dysfunctionCalc(leftList) + dysfunctionCalc(rightList)
}
}
First off, my logic is wrong,it doesn't have a merge stage and I am not sure what would be the best way to percolate the result of the base-case up the stack and compare it with the other values. Also, using recursion to solve this problem may not be the most optimal way to go since for large lists, I maybe blowing up the stack. Also, there might be stylistic issues with my code as well.
I would be great if somebody could point out other flaws and the right way to solve this problem.
Thanks
Suppose you split your list into three pieces: the item you are considering, those on the left, and those on the right. Suppose further that those on the left are in a sorted set. Now you just need to walk through the list, moving items from "right" to "considered" and from "considered" to "left"; at each point, you look at the size of the subset of the sorted set that is greater than your item. In general, the size lookup can be done in O(log(N)) as can the add-element (with a Red-Black or AVL tree, for instance). So you have O(N log N) performance.
Now the question is how to implement this in Scala efficiently. It turns out that Scala has a Red-Black tree used for its TreeSet sorted set, and the implementation is actually quite simple (here in tail-recursive form):
import collection.immutable.TreeSet
final def calcDisorder(xs: List[Int], left: TreeSet[Int] = TreeSet.empty, n: Int = 0): Int = xs match {
case Nil => n
case x :: rest => calcDisorder(rest, left + x, n + left.from(x).size)
}
Unfortunately, left.from(x).size takes O(N) time (I believe), which yields a quadratic execution time. That's no good--what you need is an IndexedTreeSet which can do indexOf(x) in O(log(n)) (and then iterate with n + left.size - left.indexOf(x) - 1). You can build your own implementation or find one on the web. For instance, I found one here (API here) for Java that does exactly the right thing.
Incidentally, the problem with doing a mergesort is that you cannot easily work cumulatively. With merging a pair, you can keep track of how out-of-order it is. But when you merge in a third list, you must see how out of order it is with respect to both other lists, which spoils your divide-and-conquer strategy. (I am not sure whether there is some invariant one could find that would allow you to calculate directly if you kept track of it.)
Here is my try, I don't use MergeSort but it seems to solve the problem:
def calcDisorderness(myList:List[Int]):Int = myList match{
case Nil => 0
case t::q => q.count(_<t) + calcDisorderness(q)
}
scala> val input = List(1,2,4,5,3,6)
input: List[Int] = List(1, 2, 4, 5, 3, 6)
scala> calcDisorderness(input)
res1: Int = 2
The question is, is there a way to have a lower complexity?
Edit: tail recursive version of the same function and cool usage of default values in function arguments.
def calcDisorderness(myList:List[Int], disorder:Int=0):Int = myList match{
case Nil => disorder
case t::q => calcDisorderness(q, disorder + q.count(_<t))
}
A solution based on Merge Sort. Not super fast, potential slowdown could be in "xs.length".
def countSwaps(a: Array[Int]): Long = {
var disorder: Long = 0
def msort(xs: List[Int]): List[Int] = {
import Stream._
def merge(left: List[Int], right: List[Int], inc: Int): Stream[Int] = {
(left, right) match {
case (x :: xs, y :: ys) if x > y =>
cons(y, merge(left, ys, inc + 1))
case (x :: xs, _) => {
disorder += inc
cons(x, merge(xs, right, inc))
}
case _ => right.toStream
}
}
val n = xs.length / 2
if (n == 0)
xs
else {
val (ys, zs) = xs splitAt n
merge(msort(ys), msort(zs), 0).toList
}
}
msort(a.toList)
disorder
}
Another solution based on Merge Sort. Very fast: no FP or for-loop.
def countSwaps(a: Array[Int]): Count = {
var swaps: Count = 0
def mergeRun(begin: Int, run_len: Int, src: Array[Int], dst: Array[Int]) = {
var li = begin
val lend = math.min(begin + run_len, src.length)
var ri = begin + run_len
val rend = math.min(begin + run_len * 2, src.length)
var ti = begin
while (ti < rend) {
if (ri >= rend) {
dst(ti) = src(li); li += 1
swaps += ri - begin - run_len
} else if (li >= lend) {
dst(ti) = src(ri); ri += 1
} else if (a(li) <= a(ri)) {
dst(ti) = src(li); li += 1
swaps += ri - begin - run_len
} else {
dst(ti) = src(ri); ri += 1
}
ti += 1
}
}
val b = new Array[Int](a.length)
var run = 0
var run_len = 1
while (run_len < a.length) {
var begin = 0
while (begin < a.length) {
val (src, dst) = if (run % 2 == 0) (a, b) else (b, a)
mergeRun(begin, run_len, src, dst)
begin += run_len * 2
}
run += 1
run_len *= 2
}
swaps
}
Convert the above code to Functional style: no mutable variable, no loop.
All recursions are tail calls, thus the performance is good.
def countSwaps(a: Array[Int]): Count = {
def mergeRun(li: Int, lend: Int, rb: Int, ri: Int, rend: Int, di: Int, src: Array[Int], dst: Array[Int], swaps: Count): Count = {
if (ri >= rend && li >= lend) {
swaps
} else if (ri >= rend) {
dst(di) = src(li)
mergeRun(li + 1, lend, rb, ri, rend, di + 1, src, dst, ri - rb + swaps)
} else if (li >= lend) {
dst(di) = src(ri)
mergeRun(li, lend, rb, ri + 1, rend, di + 1, src, dst, swaps)
} else if (src(li) <= src(ri)) {
dst(di) = src(li)
mergeRun(li + 1, lend, rb, ri, rend, di + 1, src, dst, ri - rb + swaps)
} else {
dst(di) = src(ri)
mergeRun(li, lend, rb, ri + 1, rend, di + 1, src, dst, swaps)
}
}
val b = new Array[Int](a.length)
def merge(run: Int, run_len: Int, lb: Int, swaps: Count): Count = {
if (run_len >= a.length) {
swaps
} else if (lb >= a.length) {
merge(run + 1, run_len * 2, 0, swaps)
} else {
val lend = math.min(lb + run_len, a.length)
val rb = lb + run_len
val rend = math.min(rb + run_len, a.length)
val (src, dst) = if (run % 2 == 0) (a, b) else (b, a)
val inc_swaps = mergeRun(lb, lend, rb, rb, rend, lb, src, dst, 0)
merge(run, run_len, lb + run_len * 2, inc_swaps + swaps)
}
}
merge(0, 1, 0, 0)
}
It seems to me that the key is to break the list into a series of ascending sequences. For example, your example would be broken into (1 2 4 5)(3 6). None of the items in the first list can end a pair. Now you do a kind of merge of these two lists, working backwards:
6 > 5, so 6 can't be in any pairs
3 < 5, so its a pair
3 < 4, so its a pair
3 > 2, so we're done
I'm not clear from the definition on how to handle more than 2 such sequences.
I have created solution for PE P12 in Scala but is very very slow. Can somebody can tell me why? How to optimize this? calculateDevisors() - naive approach and calculateNumberOfDivisors() - divisor function has the same speed :/
import annotation.tailrec
def isPrime(number: Int): Boolean = {
if (number < 2 || (number != 2 && number % 2 == 0) || (number != 3 && number % 3 == 0))
false
else {
val sqrtOfNumber = math.sqrt(number) toInt
#tailrec def isPrimeInternal(divisor: Int, increment: Int): Boolean = {
if (divisor > sqrtOfNumber)
true
else if (number % divisor == 0)
false
else
isPrimeInternal(divisor + increment, 6 - increment)
}
isPrimeInternal(5, 2)
}
}
def generatePrimeNumbers(count: Int): List[Int] = {
#tailrec def generatePrimeNumbersInternal(number: Int = 3, index: Int = 0,
primeNumbers: List[Int] = List(2)): List[Int] = {
if (index == count)
primeNumbers
else if (isPrime(number))
generatePrimeNumbersInternal(number + 2, index + 1, primeNumbers :+ number)
else
generatePrimeNumbersInternal(number + 2, index, primeNumbers)
}
generatePrimeNumbersInternal();
}
val primes = Stream.cons(2, Stream.from(3, 2) filter {isPrime(_)})
def calculateDivisors(number: Int) = {
for {
divisor <- 1 to number
if (number % divisor == 0)
} yield divisor
}
#inline def decomposeToPrimeNumbers(number: Int) = {
val sqrtOfNumber = math.sqrt(number).toInt
#tailrec def decomposeToPrimeNumbersInternal(number: Int, primeNumberIndex: Int = 0,
factors: List[Int] = List.empty[Int]): List[Int] = {
val primeNumber = primes(primeNumberIndex)
if (primeNumberIndex > sqrtOfNumber)
factors
else if (number % primeNumber == 0)
decomposeToPrimeNumbersInternal(number / primeNumber, primeNumberIndex, factors :+ primeNumber)
else
decomposeToPrimeNumbersInternal(number, primeNumberIndex + 1, factors)
}
decomposeToPrimeNumbersInternal(number) groupBy {n => n} map {case (n: Int, l: List[Int]) => (n, l size)}
}
#inline def calculateNumberOfDivisors(number: Int) = {
decomposeToPrimeNumbers(number) map {case (primeNumber, exponent) => exponent + 1} product
}
#tailrec def calculate(number: Int = 12300): Int = {
val triangleNumber = ((number * number) + number) / 2
val startTime = System.currentTimeMillis()
val numberOfDivisors = calculateNumberOfDivisors(triangleNumber)
val elapsedTime = System.currentTimeMillis() - startTime
printf("%d: V: %d D: %d T: %dms\n", number, triangleNumber, numberOfDivisors, elapsedTime)
if (numberOfDivisors > 500)
triangleNumber
else
calculate(number + 1)
}
println(calculate())
You could first check what is slow. Your prime calculation, for instance, is very, very slow. For each number n, you try to divide n by each each number from 5 to sqrt(n), skipping multiples of 2 and 3. Not only you do not skip numbers you already know are not primes, but even if you fix this, the complexity of this algorithm is much worse than the traditional Sieve of Eratosthenes. See one Scala implementation for the Sieve here.
That is not to say that the rest of your code isn't suboptimal as well, but I'll leave that for others.
EDIT
Indeed, indexed access to Stream is terrible. Here's a rewrite that works with Stream, instead of converting everything to Array. Also, note the remark before the first if for a possible bug in your code.
#tailrec def decomposeToPrimeNumbersInternal(number: Int, primes: Stream[Int],
factors: List[Int] = List.empty[Int]): List[Int] = {
val primeNumber = primes.head
// Comparing primeNumberIndex with sqrtOfNumber didn't make any sense
if (primeNumber > sqrtOfNumber)
factors
else if (number % primeNumber == 0)
decomposeToPrimeNumbersInternal(number / primeNumber, primes, factors :+ primeNumber)
else
decomposeToPrimeNumbersInternal(number, primes.tail, factors)
}
Slow compared to....? How do you know it's an issue with Scala, and not with your algorithm?
An admittedly quick read of the code suggests you might be recalculating primes and other values over and over. isPrimeInternal jumps out as a possible case where this might be a problem.
Your code is not compilable, some parts are missing, so I'm guessing here. Some thing that frequently hurts performance is boxing/unboxing taking place in collections. Another thing that I noted is that you cunstruct your primes as a Stream - which is a good thing - but don't take advantage of this in your isPrime function, which uses a primitive 2,3-wheel (1 and 5 mod 6) instead. I might be wrong, but try to replace it by
def isPrime(number: Int): Boolean = {
val sq = math.sqrt(number + 0.5).toInt
! primes.takeWhile(_ <= sq).exists(p => number % p == 0)
}
My scala algorithm that calculates divisors of a given number. It worked fine in the solution of
Project Euler Problem 12.
def countDivisors(numberToFindDivisor: BigInt): Int = {
def countWithAcc(numberToFindDivisor: BigInt, currentCandidate: Int, currentCountOfDivisors: Int,limit: BigInt): Int = {
if (currentCandidate >= limit) currentCountOfDivisors
else {
if (numberToFindDivisor % currentCandidate == 0)
countWithAcc(numberToFindDivisor, currentCandidate + 1, currentCountOfDivisors + 2, numberToFindDivisor / currentCandidate)
else
countWithAcc(numberToFindDivisor, currentCandidate + 1, currentCountOfDivisors, limit)
}
}
countWithAcc(numberToFindDivisor, 1, 0, numberToFindDivisor + 1)
}
calculateDivisors can be greatly improved by only checking for divisors up to the square root of the number. Each time you find a divisor below the sqrt, you also find one above.
def calculateDivisors(n: Int) = {
var res = 1
val intSqrt = Math.sqrt(n).toInt
for (i <- 2 until intSqrt) {
if (n % i == 0) {
res += 2
}
}
if (n == intSqrt * intSqrt) {
res += 1
}
res
}