I am using Scala to solve the problem documented here: https://leetcode.com/problems/number-of-islands/
Given a 2d grid map of '1's (land) and '0's (water), count the number
of islands. An island is surrounded by water and is formed by
connecting adjacent lands horizontally or vertically. You may assume
all four edges of the grid are all surrounded by water.
Example 1:
Input:
grid = [
["1","1","1","1","0"],
["1","1","0","1","0"],
["1","1","0","0","0"],
["0","0","0","0","0"] ]
Output: 1
Example 2:
Input:
grid = [
["1","1","0","0","0"],
["1","1","0","0","0"],
["0","0","1","0","0"],
["0","0","0","1","1"] ]
Output: 3
I thought it was a simple application of Breadth (or depth) First Search. However, my program produces a Memory Limit Exceeded error at Leetcode. I also ran the program on my laptop. My Mac reports it uses more than 3 GB memory, even if I'd commented out 3 lines of the test case. What consumes so much memory in my code?
// Memory OVERFLOW !!? Why ??
class Leet0200 {
def numIslands(grid: Array[Array[Char]]): Int = {
var (y, x) = getPosition(grid)
var c = 0
val dirs = Array(
Array(1, 0),
Array(-1, 0),
Array(0, 1),
Array(0, -1)
)
while ((y, x) != (-1, -1)) {
c += 1
val queue = scala.collection.mutable.Queue[(Int, Int)]()
queue.enqueue((y,x))
while (queue.nonEmpty) {
val me = queue.dequeue()
val (me_y, me_x) = me
grid(me_y)(me_x) = 'c'
for (d <- dirs) {
val (ny, nx) = (me_y + d(0), me_x+d(1))
if (ny >= 0 && ny < grid.length
&& nx >= 0 && nx < grid(0).length
&& grid(ny)(nx) == '1') {
queue.enqueue((ny, nx))
}
}
}
val newPos = getPosition(grid)
y = newPos._1
x = newPos._2
}
c
}
def getPosition(grid: Array[Array[Char]]): (Int, Int) = {
for (y <- grid.indices) {
for (x <- grid(0).indices) {
if (grid(y)(x) == '1') return (y, x)
}
}
(-1, -1)
}
}
object Leet0200 {
def main(args: Array[String]): Unit = {
val leet = new Leet0200()
println(
leet.numIslands(Array(
Array('1','1','1','1','1','0','1','1','1','1','1','1','1','1','1','0','1','0','1','1'),
Array('0','1','1','1','1','1','1','1','1','1','1','1','1','0','1','1','1','1','1','0'),
Array('1','0','1','1','1','0','0','1','1','0','1','1','1','1','1','1','1','1','1','1'),
Array('1','1','1','1','0','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1'),
Array('1','0','0','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1'),
Array('1','0','1','1','1','1','1','1','0','1','1','1','0','1','1','1','0','1','1','1'),
Array('0','1','1','1','1','1','1','1','1','1','1','1','0','1','1','0','1','1','1','1'),
Array('1','1','1','1','1','1','1','1','1','1','1','1','0','1','1','1','1','0','1','1'),
Array('1','1','1','1','1','1','1','1','1','1','0','1','1','1','1','1','1','1','1','1'),
Array('1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1'),
Array('0','1','1','1','1','1','1','1','0','1','1','1','1','1','1','1','1','1','1','1'),
Array('1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1'),
Array('1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1'),
Array('1','1','1','1','1','0','1','1','1','1','1','1','1','0','1','1','1','1','1','1'),
Array('1','0','1','1','1','1','1','0','1','1','1','0','1','1','1','1','0','1','1','1'),
Array('1','1','1','1','1','1','1','1','1','1','1','1','0','1','1','1','1','1','1','0'),
//Array('1','1','1','1','1','1','1','1','1','1','1','1','1','0','1','1','1','1','0','0'),
//Array('1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1'),
//Array('1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1'),
Array('1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1')
)))
}
}
You will have duplicates in your queue and they will snowball. When you put stuff into the queue, check if it's already in there by changing your queue to a Set.
For example, let's inspect a map:
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
Initially, getPosition() will return (0, 0).
0, 0: You will put into the queue: (0, 1) and (1, 0).
0, 1: You will put into the queue: (0, 2) and (1, 1). The queue now has: (1, 0), (0, 2), (1, 1)
1, 0: You will put into the queue: (2, 0) and (1, 1). The queue now has: (0, 2), (1, 1), (2, 0), (1, 1)
Note that there are now 2 x (1, 1) in the queue. Thus (1, 1) will be processed twice, hence adding 2 x the results to the queue (i.e. 2 x (2, 1) and 2 x (1, 2)). These results will duplicate into 4 x (2, 2). Hence a snowball effect.
This will be the snowball effect consuming memory in your queue. If you use a Set, then you won't have the snowball effect as the same position will not get added again.
An alternate solution is to use your marker when adding to the queue instead of when processing like the code below:
val queue = scala.collection.mutable.Queue[(Int, Int)]()
queue.enqueue((y,x))
grid(y)(x) = 'c'; // <--- moved to
while (queue.nonEmpty) {
val me = queue.dequeue()
val (me_y, me_x) = me
// grid(me_y)(me_x) = 'c' // <--- moved from
for (d <- dirs) {
val (ny, nx) = (me_y + d(0), me_x+d(1))
if (ny >= 0 && ny < grid.length
&& nx >= 0 && nx < grid(0).length
&& grid(ny)(nx) == '1') {
queue.enqueue((ny, nx))
grid(ny)(nx) = 'c'; // <--- moved to
}
}
}
Because you do the grid(ny)(nx) == '1' check, the same position will not be added to the queue multiple times.
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]));
I want to talk about how to which proceed.
1. Incorrect usage of Scala. I should try to more improve the code.
2. The efficiency of the algorithm is poor. I should think of an efficient algorithm.
Goal:
Can quickly calculate the max number from more than 1,000 Partition Numbers collections.
Partition Number:
e.g.,
5 -> (5), (1, 4), (2, 3), (1, 1, 3), (1, 2, 2), (1, 1, 1, 2), (1, 1, 1, 1, 1)
I ask that "I want to convert from Python to Scala that Partition Function using Vector", and I was taught to use Stream yesterday.
I fixed code, I can use 10, 50, and so on. But using big numbers(e. g., 100, 1,000 or 10,000) weren't calculate max number.
It calculate from Stream.last to Stream.head.
In my understanding that Stream type can add an element at the head only, so the order of the numbers is reversed form the fist code.
code
import scala.math.floor
class PartitionNumbers(startNum: Int, point: Int) {
var maxNum = 0
var tmpNum = 0
private def appendOnes(n: Int, s: Stream[Int] = Stream.empty[Int]): Stream[Int] = {
if (n == 0) s
else appendOnes(n - 1, 1 #:: s)
}
private def partition(n: Int, k: Int, tmpStream: Stream[Int] = Stream.empty): Int = {
if (n == 0) tmpNum = calculate(tmpStream)
else if (n == 1 | k == 1) tmpNum = calculate(appendOnes(n))
else {
if (n >= k) partition(n - k, k, k #:: tmpStream)
partition(n, k - 1, tmpStream)
}
if (maxNum < tmpNum) maxNum = tmpNum
maxNum
}
def searchMax(n: Int = point): Int = {
partition(n, n)
}
def calculate(usePointsStream: Stream[Int], num: Int = startNum): Int = {
if (usePointsStream.isEmpty) {
num
} else {
calculate(usePointsStream.init, floor(num * (100 + usePointsStream.last) / 100).toInt)
}
}
}
output example
val pn_1 = new PartitionNumbers(100, 10)
println(pn_1.searchMax()) // -> 110
val pn_2 = new PartitionNumbers(1000, 50)
println(pn_2.searchMax()) // -> 1630
val pn_3 = new PartitionNumbers(10000, 100)
println(pn_3.searchMax()) // Can't calculate within 3 minutes using Ryzen 7 2700X.
It is not a homework problem. I am just curious about this problem. And my approach is simple brute-force :-)
My brute-force C++ code:
int main()
{
ll l,r;
cin>>l>>r;
ll f=0;
ll i=l;
while(i<=r)
{
ll j=0;
string s;
ll c=0;
s=to_string(i);
// cout<<s<<" ";
ll x=s.length();
if(x==1)
{
c=0;
}
else
{
j=0;
//whil
while(j<=x-2)
{
string b,g;
b="1";
g="1";
b=s[j];
g=s[j+1];
ll k1,k2;
k1=stoi(b);
k2=stoi(g);
if(__gcd(k1,k2)==1)
{
c=1;
break;
}
j++;
}
}
ll d=0;
j=0;
while(j<=x-1)
{
if( s[j]=='2' || s[j]=='3' || s[j]=='5' || s[j]=='7')
{
string b;
b="1";
b=s[j];
ll k1=stoi(b);
if(i%k1==0)
{
//d=0;
}
else
{
d=1;
break;
}
}
j++;
}
if(c==1 || d==1)
{
// cout<<"NO";
}
else
{
f++;
// cout<<"PR";
}
// cout<<"\n";
i++;
}
cout<<f;
return 0;
}
You are given 2 integers 'L' and 'R' . You are required to find the count of all the PR numbers in the range 'L' to 'R' inclusively. PR number are the numbers which satisfy following properties:
No pair of adjacent digits are co-prime i.e. adjacent digits in a PR number will not be co-prime to each other.
PR number is divisible by all the single digit prime numbers which occur as a digit in the PR number.
Note: Two numbers 'a' and 'b' are co-prime, if gcd(a,b)=1.
Also, gcd(0,a)=a;
Example:
Input: [2,5].
Output: '4'.
(Note: '1' is not a prime-number, though its very common)
(All the integers: '2','3','4','5') satisfy the condition of PR numbers :-)
Constraints on 'L','R': 1 <= L, R <= 10^18
What can be the the most efficient algorithm to solve this ?
Note: This will solve only part 1 which is No pair of adjacent digits are co-prime i.e. adjacent digits in a PR number will not be co-prime to each other.
Here is a constructive approach in python: instead of going throught all numbers in range and filtering by conditions, we will just construct all numbers that satisfy the condition. Note that if we have a valid sequence of digits, for it to continue being valid only the rightmost digit matters in order to decide what the next digit will be.
def ways(max_number, prev_digit, current_number):
if current_number > max_number:
return 0
count = 1
if prev_digit == 0:
if current_number != 0:
count += ways(max_number, 0, current_number * 10)
for i in range(2, 10):
count += ways(max_number, i, current_number * 10 + i)
if prev_digit == 2 or prev_digit == 4 or prev_digit == 8:
for i in [0, 2, 4, 6, 8]:
count += ways(max_number, i, current_number * 10 + i)
if prev_digit == 3 or prev_digit == 9:
for i in [0, 3, 6, 9]:
count += ways(max_number, i, current_number * 10 + i)
if prev_digit == 5 or prev_digit == 7:
count += ways(max_number, 0, current_number * 10)
count += ways(max_number, prev_digit, current_number * 10 + prev_digit)
if prev_digit == 6:
for i in [0, 2, 3, 4, 6, 8, 9]:
count += ways(max_number, i, current_number * 10 + i)
return count
As we are generating all valid numbers up to max_number without any repeats, the complexity of this function is O(amount of numbers between 0 and max_number that satisfy condition 1). To calculate the range a to b, we just need to do ways(b) - ways(a - 1).
Takes less than 1 second to caculate these numbers from 0 to 1 million, as there are only 42935 numbers that satisfy the result. As there are few numbers that satisfy the condition, we can then check if they are multiple of its prime digits to satisfy also condition 2. I leave this part up to the reader as there are multiple ways to do it.
TL;DR: This is more commonly called "digit dynamic programming with bitmask"
In more competitive-programming-familiar terms, you'd compute dp[n_digit][mod_2357][is_less_than_r][digit_appeared][last_digit] = number of numbers with n_digit digits (including leading zeroes), less than the number formed by first n_digit digits of R and with the other properties match. Do it twice with R and L-1 then take the difference. The number of operations required would be about 19 (number of digits) * 210 (mod) * 2 * 24 (it's only necessary to check for appearance of single-digit primes) * 10 * 10, which is obviously manageable by today computers.
Think about how you'd check whether a number is valid.
Not the normal way. Using a finite state automaton that take the input from left to right, digit by digit.
For simplicity, assume the input has a fixed number of digits (so that comparison with L/R is easier. This is possible because the number has at most as many digits as R).
It's necessary for each state to keep track of:
which digit appeared in the number (use a bit mask, there are 4 1-digit primes)
is the number in range [L..R] (either this is guaranteed to be true/false by the prefix, otherwise the prefix matches with that of L/R)
what is the value of the prefix mod each single digit prime
the most recent digit (to check whether all pairs of consecutive digits are coprime)
After the finite state automaton is constructed, the rest is simple. Just use dynamic programming to count the number of path to any accepted state from the starting state.
Remark: This method can be used to count the number of any type of object that can be verified using a finite state automaton (roughly speaking, you can check whether the property is satisfied using a program with constant memory usage, and takes the object piece-by-piece in some order)
We need a table where we can look up the count of suffixes that would match a prefix to construct valid numbers. Given a prefix's
right digit
prime combination
mod combination
and a suffix length, we'd like the count of suffixes that have searchable:
left digit
length
prime combination
mod combination
I started coding in Python, then switched to JavaScript to be able to offer a snippet. Comments in the code describe each lookup table. There are a few of them to allow for faster enumeration. There are samples of prefix-suffix calculations to illustrate how one can build an arbitrary upper-bound using the table, although at least some, maybe all of the prefix construction and aggregation could be made during the tabulation.
function gcd(a,b){
if (!b)
return a
else
return gcd(b, a % b)
}
// (Started writing in Python,
// then switched to JavaScript...
// 'xrange(4)' -> [0, 1, 2, 3]
// 'xrange(2, 4)' -> [2, 3]
function xrange(){
let l = 0
let r = arguments[1] || arguments[0]
if (arguments.length > 1)
l = arguments[0]
return new Array(r - l).fill(0).map((_, i) => i + l)
}
// A lookup table and its reverse,
// mapping each of the 210 mod combinations,
// [n % 2, n % 3, n % 5, n % 7], to a key
// from 0 to 209.
// 'mod_combs[0]' -> [0, 0, 0, 0]
// 'mod_combs[209]' -> [1, 2, 4, 6]
// 'mod_keys[[0,0,0,0]]' -> 0
// 'mod_keys[[1,2,4,6]]' -> 209
let mod_combs = {}
let mod_keys = {}
let mod_key_count = 0
for (let m2 of xrange(2)){
for (let m3 of xrange(3)){
for (let m5 of xrange(5)){
for (let m7 of xrange(7)){
mod_keys[[m2, m3, m5, m7]] = mod_key_count
mod_combs[mod_key_count] = [m2, m3, m5, m7]
mod_key_count += 1
}
}
}
}
// The main lookup table built using the
// dynamic program
// [mod_key 210][l_digit 10][suffix length 20][prime_comb 16]
let table = new Array(210)
for (let mk of xrange(210)){
table[mk] = new Array(10)
for (let l_digit of xrange(10)){
table[mk][l_digit] = new Array(20)
for (let sl of xrange(20)){
table[mk][l_digit][sl] = new Array(16).fill(0)
}
}
}
// We build prime combinations from 0 (no primes) to
// 15 (all four primes), using a bitmask of up to four bits.
let prime_set = [0, 0, 1<<0, 1<<1, 0, 1<<2, 0, 1<<3, 0, 0]
// The possible digits that could
// follow a digit
function get_valid_digits(digit){
if (digit == 0)
return [0, 2, 3, 4, 5, 6, 7, 8, 9]
else if ([2, 4, 8].includes(digit))
return [0, 2, 4, 6, 8]
else if ([3, 9].includes(digit))
return [0, 3, 6, 9]
else if (digit == 6)
return [0, 2, 3, 4, 6, 8, 9]
else if (digit == 5)
return [0, 5]
else if (digit == 7)
return [0, 7]
}
// Build the table bottom-up
// Single digits
for (let i of xrange(10)){
let mod_key = mod_keys[[i % 2, i % 3, i % 5, i % 7]]
let length = 1
let l_digit = i
let prime_comb = prime_set[i]
table[mod_key][l_digit][length][prime_comb] = 1
}
// Everything else
// For demonstration, we just table up to 6 digits
// since either JavaScript, this program, or both seem
// to be too slow for a full demo.
for (let length of xrange(2, 6)){
// We're appending a new left digit
for (let new_l_digit of xrange(0, 10)){
// The digit 1 is never valid
if (new_l_digit == 1)
continue
// The possible digits that could
// be to the right of our new left digit
let ds = get_valid_digits(new_l_digit)
// For each possible digit to the right
// of our new left digit, iterate over all
// the combinations of primes and remainder combinations.
// The ones that are populated are valid paths, the
// sum of which can be aggregated for each resulting
// new combination of primes and remainders.
for (let l_digit of ds){
for (let p_comb of xrange(16)){
for (let m_key of xrange(210)){
new_prime_comb = prime_set[new_l_digit] | p_comb
// suffix's remainder combination
let [m2, m3, m5, m7] = mod_combs[m_key]
// new remainder combination
let m = Math.pow(10, length - 1) * new_l_digit
let new_mod_key = mod_keys[[(m + m2) % 2, (m + m3) % 3, (m + m5) % 5, (m + m7) % 7]]
// Aggregate any populated entries into the new
// table entry
table[new_mod_key][new_l_digit][length][new_prime_comb] += table[m_key][l_digit][length - 1][p_comb]
}
}
}
}
}
// If we need only a subset of the mods set to
// zero, we need to check all instances where
// this subset is zero. For example,
// for the prime combination, [2, 3], we need to
// check all mod combinations where the first two
// are zero since we don't care about the remainders
// for 5 and 7: [0,0,0,0], [0,0,0,1],... [0,0,4,6]
// Return all needed combinations given some
// predetermined, indexed remainders.
function prime_comb_to_mod_keys(remainders){
let mod_map = [2, 3, 5, 7]
let mods = []
for (let i of xrange(4))
mods.push(!remainders.hasOwnProperty(i) ? mod_map[i] - 1 : 0)
function f(ms, i){
if (i == ms.length){
for (let idx in remainders)
ms[idx] = remainders[idx]
return [mod_keys[ms]]
}
let result = []
for (let m=ms[i] - 1; m>=0; m--){
let _ms = ms.slice()
_ms[i] = m
result = result.concat(f(_ms, i + 1))
}
return result.concat(f(ms, i + 1))
}
return f(mods, 0)
}
function get_matching_mods(prefix, len_suffix, prime_comb){
let ps = [2, 3, 5, 7]
let actual_prefix = Math.pow(10, len_suffix) * prefix
let remainders = {}
for (let i in xrange(4)){
if (prime_comb & (1 << i))
remainders[i] = (ps[i] - (actual_prefix % ps[i])) % ps[i]
}
return prime_comb_to_mod_keys(remainders)
}
// A brute-force function to check the
// table is working. Returns a list of
// valid numbers of 'length' digits
// given a prefix.
function confirm(prefix, length){
let result = [0, []]
let ps = [0, 0, 2, 3, 0, 5, 0, 7, 0, 0]
let p_len = String(prefix).length
function check(suffix){
let num = Math.pow(10, length - p_len) * prefix + suffix
let temp = num
prev = 0
while (temp){
let d = temp % 10
if (d == 1 || gcd(prev, d) == 1 || (ps[d] && num % d))
return [0, []]
prev = d
temp = ~~(temp / 10)
}
return [1, [num]]
}
for (suffix of xrange(Math.pow(10, length - p_len))){
let [a, b] = check(suffix)
result[0] += a
result[1] = result[1].concat(b)
}
return result
}
function get_prime_comb(prefix){
let prime_comb = 0
while (prefix){
let d = prefix % 10
prime_comb |= prime_set[d]
prefix = ~~(prefix / 10)
}
return prime_comb
}
// A function to test the table
// against the brute-force method.
// To match a prefix with the number
// of valid suffixes of a chosen length
// in the table, we want to aggregate all
// prime combinations for all valid digits,
// where the remainders for each combined
// prime combination (prefix with suffix)
// sum to zero (with the appropriate mod).
function test(prefix, length, show=false){
let r_digit = prefix % 10
let len_suffix = length - String(prefix).length
let prefix_prime_comb = get_prime_comb(prefix)
let ds = get_valid_digits(r_digit)
let count = 0
for (let l_digit of ds){
for (let prime_comb of xrange(16)){
for (let i of get_matching_mods(prefix, len_suffix, prefix_prime_comb | prime_comb)){
let v = table[i][l_digit][len_suffix][prime_comb]
count += v
}
}
}
let c = confirm(prefix, length)
return `${ count }, ${ c[0] }${ show ? ': ' + c[1] : '' }`
}
// Arbitrary prefixes
for (let length of [3, 4]){
for (let prefix of [2, 30]){
console.log(`prefix, length: ${ prefix }, ${ length }`)
console.log(`tabled, brute-force: ${ test(prefix, length, true) }\n\n`)
}
}
let length = 6
for (let l_digit=2; l_digit<10; l_digit++){
console.log(`prefix, length: ${ l_digit }, ${ length }`)
console.log(`tabled, brute-force: ${ test(l_digit, length) }\n\n`)
}
A vampire number is defined here https://en.wikipedia.org/wiki/Vampire_number. A number V is a vampire number if:
It can be expressed as X*Y such that X and Y have N/2 digits each where N is the number of digits in V
Both X & Y should not have trailing zeros
X & Y together should have the same digits as V
I came up with a solution,
strV = sort(toString(V))
for factor <- pow(10, N/2) to sqrt(V)
if factor divides V
X <- factor
Y <- V/factor
if X and Y have trailing zeros
continue
checkStr = sort(toString(X) + toString(Y))
if checkStr equals strV return true
Another possible solution is to permute the string represented by V and split it into half and check if its a vampire number. Which one is the best way to do so?
The algorithm I propose here will not go through all permutations of digits. It will eliminate possibilities as fast as possible so that only a fraction of permutations will actually be tested.
Algorithm explained by example
Here is how it works based on example number 125460. If you are fine with reading the code directly, then you can skip this (long) part:
At first the two fangs (i.e. vampire factors) are obviously not known, and the problem can be represented as follows:
?**
X ?**
-------
=125460
For the left most digit of the first factor (marked with ?) we could choose any of the digits 0,1,2,5,4, or 6. But on closer analysis 0 would not be a viable possibility, as the product would never reach more than a 5-digit number. So it would be a waste of time to go through all permutations of digits that start with a zero.
For the left most digit of the second factor (also marked with ?), the same is true. However, when looking at the combinations, we can again filter out some pairs that cannot contribute to reaching the target product. For instance, this combination should be discarded:
1**
X 2**
-------
=125460
The greatest number that can be achieved with these digits is 199x299 = 59501 (ignoring the fact that we don't even have a 9), which is not even half of the desired number. So we should reject the combination (1, 2). For the same reason, the pair (1, 5) can be discarded for taking these positions. Similarly, the pairs (4, 5), (4, 6), and (5, 6) can be rejected as well, because they yield a too large product (>= 200000). I will call this kind of a test -- where it is determined whether the target number is within reach for a certain chosen digit pair, the "range test".
At this stage there is no difference between the first and the second fang, so we should also not have to investigate pairs where the second digit is smaller than the first, because they mirror a pair that would already have been investigated (or rejected).
So of all the possible pairs that could take up this first position (there are 30 possibilities to take 2 digits from a set of 6 digits), only the following 4 need to be investigated:
(1, 6), (2, 4), (2, 5), (2, 6)
In a more elaborate notation this means we are limiting the search to these number patterns:
1** 2** 2** 2**
X 6** X 4** X 5** X 6**
------- ------- ------- -------
=125460 =125460 =125460 =125460
A B C D
It is clear that this reduction of possibilities before even looking at the other positions greatly reduces the search tree.
The algorithm will take each of these 4 possibilities in order, and for each will check the possibilities for the next digit position. So first configuration A is analysed:
1?*
X 6?*
-------
=125460
The pairs that are available for the ?-marked positions are these 12:
(0, 2), (0, 4), (0, 5)
(2, 0), (2, 4), (2, 5)
(4, 0), (4, 2), (4, 5)
(5, 0), (5, 2), (5, 4)
Again, we can eliminate pairs by applying the range test. Let's take for instance the pair (5, 4). This would mean we had factors 15* and 64* (where * is an unknown digit at this point). The product of these two will be maximised with 159 * 649, i.e. 103191 (again ignoring the fact we do not even have a 9 available): this is too low for reaching the target, so this pair can be ignored. By further applying the range test, all these 12 pairs can be discarded, and so the search within configuration A stops here: there is no solution there.
Then the algorithm moves to configuration B:
2?*
X 4?*
-------
=125460
Again, the range test is applied to the possible pairs for the second position, and again it turns out none of these pairs passes the test: for instance (5, 6) can never represent a greater product than 259 * 469 = 121471, which is (only just) too small.
Then the algorithm moves to option C:
2?*
X 5?*
-------
=125460
Of all 12 possible pairs, only the following survive the range test: (4, 0), (4, 1), (6, 0), (6, 1). So now we have the following second-level configurations:
24* 24* 26* 26*
X 50* X 51* X 50* X 51*
------- ------- ------- -------
=125460 =125460 =125460 =125460
Ca Cb Cc Cd
In configuration Ca, there is no pair that passes the range test.
In configuration Cb, the pair (6, 0) passes, and leads to a solution:
246
X 510
-------
=125460
At this point the algorithm stops searching. The outcome is clear. In total the number of configurations looked at is very small compared to a brute force permutation checking algorithm. Here is a visualisation of the search tree:
*-+-- (1, 6)
|
+-- (2, 4)
|
+-- (2, 5) -+-- (4, 0)
| |
| +-- (4, 1) ---- (6, 0) = success: 246 * 510
/ /
| +-- (6, 0)
| |
| +-- (6, 1)
|
+-- (2, 6) ---- (0, 1) ---- (4, 5) = success: 204 * 615
The variants below / are only for showing what else the algorithm would have done, if there had not been a solution found. But in this actual case, that part of the search tree was actually never followed.
I have no clear idea of the time complexity, but it seems to run quite well for larger numbers, showing that the elimination of digits at an early stage makes the width of the search tree quite narrow.
Here is a live JavaScript implementation, which also runs some test cases when it it is activated (and it has a few other optimisations -- see code comments).
/*
Function: vampireFangs
Arguments:
vampire: number to factorise into two fangs, if possible.
Return value:
Array with two fangs if indeed the argument is a vampire number.
Otherwise false (not a vampire number) or null (argument too large to
compute)
*/
function vampireFangs(vampire) {
/* Function recurse: for the recursive part of the algorithm.
prevA, prevB: partial, potential fangs based on left-most digits of the given
number
counts: array of ten numbers representing the occurrence of still
available digits
divider: power of 100, is divided by 100 each next level in the search tree.
Determines the number of right-most digits of the given number that
are ignored at first in the algorithm. They will be considered in
deeper levels of recursion.
*/
function recurse(vampire, prevA, prevB, counts, divider) {
if (divider < 1) { // end of recursion
// Product of fangs must equal original number and fangs must not both
// end with a 0.
return prevA * prevB === vampire && (prevA % 10 + prevB % 10 > 0)
? [prevA, prevB] // Solution found
: false; // It's not a solution
}
// Get left-most digits (multiple of 2) of potential vampire number
var v = Math.floor(vampire/divider);
// Shift decimal digits of partial fangs to the left to make room for
// the next digits
prevA *= 10;
prevB *= 10;
// Calculate the min/max A digit that can potentially contribute to a
// solution
var minDigA = Math.floor(v / (prevB + 10)) - prevA;
var maxDigA = prevB ? Math.floor((v + 1) / prevB) - prevA : 9;
if (maxDigA > 9) maxDigA = 9;
for (var digA = minDigA; digA <= maxDigA; digA++) {
if (!counts[digA]) continue; // this digit is not available
var fangA = prevA + digA;
counts[digA]--;
// Calculate the min/max B digit that can potentially contribute to
// a solution
var minDigB = Math.floor(v / (fangA + 1)) - prevB;
var maxDigB = fangA ? (v + 1) / fangA - prevB : 9;
// Don't search mirrored A-B digits when both fangs are equal until now.
if (prevA === prevB && digA > minDigB) minDigB = digA;
if (maxDigB > 9) maxDigB = 9;
for (var digB = minDigB; digB <= Math.min(maxDigB, 9); digB++) {
if (!counts[digB]) continue; // this digit is not available
var fangB = prevB + digB;
counts[digB]--;
// Recurse by considering the next two digits of the potential
// vampire number, for finding the next digits to append to
// both partial fangs.
var result = recurse(vampire, fangA, fangB, counts, divider / 100);
// When one solution is found: stop searching & exit search tree.
if (result) return result; // solution found
// Restore counts
counts[digB]++;
}
counts[digA]++;
}
}
// Validate argument
if (typeof vampire !== 'number') return false;
if (vampire < 0 || vampire % 1 !== 0) return false; // not positive and integer
if (vampire > 9007199254740991) return null; // beyond JavaScript precision
var digits = vampire.toString(10).split('').map(Number);
// A vampire number has an even number of digits
if (!digits.length || digits.length % 2 > 0) return false;
// Register per digit (0..9) the frequency of that digit in the argument
var counts = [0,0,0,0,0,0,0,0,0,0];
for (var i = 0; i < digits.length; i++) {
counts[digits[i]]++;
}
return recurse(vampire, 0, 0, counts, Math.pow(10, digits.length - 2));
}
function Timer() {
function now() { // try performance object, else use Date
return performance ? performance.now() : new Date().getTime();
}
var start = now();
this.spent = function () { return Math.round(now() - start); }
}
// I/O
var button = document.querySelector('button');
var input = document.querySelector('input');
var output = document.querySelector('pre');
button.onclick = function () {
var str = input.value;
// Convert to number
var vampire = parseInt(str);
// Measure performance
var timer = new Timer();
// Input must be valid number
var result = vampire.toString(10) !== str ? null
: vampireFangs(vampire);
output.textContent = (result
? 'Vampire number. Fangs are: ' + result.join(', ')
: result === null
? 'Input is not an integer or too large for JavaScript'
: 'Not a vampire number')
+ '\nTime spent: ' + timer.spent() + 'ms';
}
// Tests (numbers taken from wiki page)
var tests = [
// Negative test cases:
[1, 999, 126000, 1023],
// Positive test cases:
[1260, 1395, 1435, 1530, 1827, 2187, 6880,
102510, 104260, 105210, 105264, 105750, 108135,
110758, 115672, 116725, 117067, 118440,
120600, 123354, 124483, 125248, 125433, 125460, 125500,
13078260,
16758243290880,
24959017348650]
];
tests.forEach(function (vampires, shouldBeVampire) {
vampires.forEach(function (vampire) {
var isVampire = vampireFangs(vampire);
if (!isVampire !== !shouldBeVampire) {
output.textContent = 'Unexpected: vampireFangs('
+ vampire + ') returns ' + JSON.stringify(isVampire);
throw 'Test failed';
}
});
});
output.textContent = 'All tests passed.';
N: <input value="1047527295416280"><button>Vampire Check</button>
<pre></pre>
As JavaScript uses 64 bit floating point representation, the above snippet only accepts to numbers up to 253-1. Above that limit there would be loss of precision and consequently unreliable results.
As Python does not have such limitation, I also put a Python implementation on eval.in. That site has a limitation on execution times, so you'd have to run it elsewhere if that becomes an issue.
In pseudocode:
if digitcount is odd return false
if digitcount is 2 return false
for A = each permutation of length digitcount/2 selected from all the digits,
for B = each permutation of the remaining digits,
if either A or B starts with a zero, continue
if both A and B end in a zero, continue
if A*B == the number, return true
There are a number of optimizations that could still be performed here, mostly in terms of ensuring that each possible pair of factors is tried only once. In other words, how to best check for repeating digits when selecting permutations?
But that's the gist of the algorithm I would use.
P.S.: You're not looking for primes, so why use a primality test? You just care about whether these are vampire numbers; there are only a very few possible factors. No need to check all the numbers up to sqrt(number).
Here are some suggestions:
First a simple improvement: if the number of digits is < 4 or odd return false (or if v is negative too).
You don't need to sort v, it is enough to count how many times each digit occurs O(n).
You don't have to check each number, only the combinations that are possible with the digits. This could be done by backtracking and significantly reduces the amount of numbers that have to be checked.
The final sort to check if all digits were used isn't needed either, just add up the used digits of both numbers and compare with the occurences in v.
Here is the code for a JS-like language with integers that never overflow, the V parameter is an integer string without leading 0s:
Edit: As it turns out the code is not only JS-like, but valid JS code and it had no problem to decide that 1047527295416280 is indeed a vampire number (jsfiddle).
var V, v, isVmp, digits, len;
function isVampire(numberString) {
V = numberString;
if (V.length < 4 || V.length % 2 == 1 )
return false;
v = parseInt(V);
if (v < 0)
return false;
digits = countDigits(V);
len = V.length / 2;
isVmp = false;
checkNumbers();
return isVmp;
}
function countDigits(s) {
var offset = "0".charCodeAt(0);
var ret = [0,0,0,0,0,0,0,0,0,0];
for (var i = 0; i < s.length; i++)
ret[s.charCodeAt(i) - offset]++;
return ret;
}
function checkNumbers(number, depth) {
if (isVmp)
return;
if (typeof number == 'undefined') {
for (var i = 1; i < 10; i++) {
if (digits[i] > 0) {
digits[i]--;
checkNumbers(i, len - 1);
digits[i]++;
}
}
} else if (depth == 0) {
if (v % number == 0) {
var b = v / number;
if (number % 10 != 0 || b % 10 != 0) {
var d = countDigits('' + b);
if (d[0] == digits[0] && d[1] == digits[1] && d[2] == digits[2] &&
d[3] == digits[3] && d[4] == digits[4] && d[5] == digits[5] &&
d[6] == digits[6] && d[7] == digits[7] && d[8] == digits[8] &&
d[9] == digits[9])
isVmp = true;
}
}
} else {
for (var i = 0; i < 10; i++) {
if (digits[i] > 0) {
digits[i]--;
checkNumbers(number * 10 + i, depth - 1);
digits[i]++;
}
}
}
}
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.