Given a number, find whether it is brilliant or not - algorithm

It's a programming puzzle which goes like: "A number is said to be brilliant if the product of all digits of its substrings have a unique value."
Example : 263 (2, 6, 3, 2*6 = 12, 6*3 = 18) is brilliant.
But 236 (2, 3, 6, 2*3 = 6, 3*6 = 18) is not brilliant.
We take only substrings, not subsequences.
I was thinking maybe we can apply Dynamic Programming here because of repeated product calculations? What other solutions can we have for it? (This isn't a homework question.)

Here's one way of solving it using dynamic programming:
Assume we have the number d0 d1 ... dN as input.
The idea is to create a table, where cell (i, j) store the product di · di+1 · ... · dj. This can be done efficiently since the cell at (i, j) can be computed by multiplying the number at (i-1, j) by di.
Since i (the start index) must be less than or equal to j (the end index), we'll focus on the lower left triangle of the table.
After generating the table, we check for duplicate entries.
Here's a concrete example solution for input 2673:
We allocate a matrix, M, with dimensions 4 × 4.
We start by filling in the diagonals, Mi,i with di:
We then go row by row, and fill in Mi,j with di ·Mi-1,j
The result looks like
To check for duplicates, we collect the products (2, 12, 6, 84, 42, 7, 252, 126, 21, 3), sort them (2, 3, 6, 7, 12, 21, 42, 84, 126, 252), and loop through to see if two consecutive numbers are equal. If so we return false, otherwise true.
In Java code:
Here's a working DP solution, O(n2).
public static boolean isColorful(int num) {
// Some initialization
String str = "" + num;
int[] digits = new int[str.length()];
for (int i = 0; i < str.length(); i++)
digits[i] = str.charAt(i) - '0';
int[][] dpmatrix = new int[str.length()][str.length()];
// Fill in diagonal: O(N)
for (int i = 0; i < digits.length; i++)
dpmatrix[i][i] = digits[i];
// Fill in lower left triangle: O(N^2)
for (int i = 0; i < str.length(); i++)
for (int j = 0; j < i; j++)
dpmatrix[i][j] = digits[i] * dpmatrix[i-1][j];
// Check for dups: O(N^2)
int[] nums = new int[digits.length * (digits.length+1) / 2];
for (int i = 0, j = 0; i < digits.length; i++, j += i)
System.arraycopy(dpmatrix[i], 0, nums, j, i+1);
Arrays.sort(nums);
for (int i = 0; i < nums.length - 1; i++)
if (nums[i] == nums[i+1])
return false;
return true;
}
For DP-interested readers I can recommend the somewhat similar question/answer over here:
Find the number of occurrences of a subsequence in a string

Using dynamic programming is probably the way to go:
Instead of calculating all O(n^2) substrings, and then using ~n multiplication commands to calculate each of them, store the results of previous caclulation in a matrix M, where M(i,j) is the result of the substring of length j, starting from position i.
(i.e, if your number is 123456789, then M(1,5) is 5!, and M(1,6) is 6!, which only requires multiplying M(1,5) by 6 - constant work)
This will improve the running time from O(n^3) for n digits to O(n^2).

A dynamic programming solution is really not necessary, as there are no brilliant numbers with a large number of digits (if any digit appears more than once, the number is not brilliant).
Here is a list of every brilliant number. There are 57,281 total.
This file took less than a second to generate on my PC, even without using dynamic programming :)

if we don't consider the number as a large string then hashing can help;
int brill(int A) {
map<long long int,bool> m;
vector<int> arr(10,0);
int i=0;
while(A){
arr[i++]=A%10;
A/=10;
}
for(int j=0;j<i;j++){
long long int temp=1;
for(int k=j;k>=0;k--){
temp*=arr[k];
if(m.find(temp)!=m.end()){
return 0;
}
else{
m[temp]=true;
}
}
}
return 1;
}

n is the string containing the number.
Since the number can't be greater than 8 digits before a failure state its O(1).
function isBrill(n) {
var set = {};
set[parseInt(n.charAt(0))] = true;
for(var i=0; i < n.length - 1; i++) {
var a = parseInt(n.charAt(i));
var b = parseInt(n.charAt(i+1));
if(set[b] === true) { return false; }
set[b] = true;
if(set[a * b] === true) { return false; }
set[a * b] = true;
}
return true;
}
isBrill("263"); // true
isBrill("236"); // false

Related

K-th Smallest in Lexicographical Order

Given integers n and k, find the lexicographically k-th smallest integer in the range from 1 to n.
Note: 1 ≤ k ≤ n ≤ 109.
Example:
Input:
n: 13 k: 2
Output:
10
Explanation:
The lexicographical order is [1, 10, 11, 12, 13, 2, 3, 4, 5, 6, 7, 8, 9], so the second smallest number is 10.
I have written a code which works fine but when I give very high input it takes a lot of time to execute and hence time out. Could some one please suggest me how i can make it more efficient.
Thanks!!
public class Solution {
class MyComp implements Comparator<Integer>{
#Override
public int compare(Integer n1, Integer n2) {
return String.valueOf(n1).compareTo(String.valueOf(n2));
}
}
public int findKthNumber(int n, int k) {
if(n==0 || k ==0 || k > n) return 0;
int[] tracker = new int[9];
Arrays.fill(tracker,0);
Map<Integer,TreeSet<Integer>> map = new HashMap<Integer,TreeSet<Integer>>();
for(int i =1;i<=n;i++){
String prefix = String.valueOf(i);
int currIndex = Integer.parseInt(prefix.substring(0,1));
//Update count
tracker[currIndex-1] = tracker[currIndex-1] + 1;
if(map.containsKey(currIndex)){
TreeSet<Integer> set = map.get(currIndex);
set.add(i);
map.put(currIndex,set);
}else{
TreeSet<Integer> set = new TreeSet<Integer>(new MyComp());
set.add(i);
map.put(currIndex,set);
}
}
// counter to check the if we reach near by K
int count =1;
for(int i=0;i<9 ;i++ ){
int lookUp = i+1;
int val = tracker[i];
if( count + map.get(lookUp).size() > k){
for(int res : map.get(lookUp)){
if(count == k) return res;
count++;
}
}
count = count + map.get(lookUp).size();
}
return 0;
}
}
You can use QuickSort for finding location. That will give you nlogn but will be much quicker n simpler.
Psudo code
-> pick random number of out list.
-> put all element lesser than that one side,higher than that number other side.
->if the number of lesser elements are k-1, you got the answer.
->if lesser elements are less than k-1, apply same algo on right side.
-> if lesser numbers of elements are greater than k, apply same algo on left side.
You can do this inplace
Best time complexity is o (1)
Worst time complexity is o (n*n)
But it gives very stable performance for multiple iteration
Random nature works on all sort of data
Let me know if any of the steps are not clear :)
For such small numbers you could generate all Strings in an array, sort it and return the k-th entry:
String[] arr = new String[n];
for (int i = 0; i < n; i++)
arr[i] = String.valueOf(n + 1);
Arrays.sort(arr);
return Integer.parseInt(arr[k - 1]);
It seems to be much easier than counting to the k-th entry. You don't need to sort the whole array, because you only need to find the k-th smallest entry, anyway for those small numbers it doesn't matter.
Or even better use an integer array and the comparator you already created:
Integer[] arr = new Integer[n];
for (int i = 0; i < n; i++)
arr[i] = i + 1;
Arrays.sort(arr, MyComp);
return arr[k - 1].intValue();

Counting the number of subset solution in the subset sum (knapsack) with dynamic programming

So I want to know how to count all the solutions for a knapsack problem. Namely I'm interested in finding the number of possible subsets from a set of numbers that have the maximum size of K.
e.g we have a set of items of size {3, 2, 5, 6, 7} and the max size is K = 13. So the solutions are {5, 6, 2} and {6, 7}. On the other hand there are two solutions; I want my dynamic programming algorithm to report there are two possible solutions.
This can be done with dynamic programming. The basic strategy is to build a memorization table, d[i][j], which stores the number of combinations using the first j numbers that sum to i. Note that j = 0 represents an empty set of numbers. Here is a sample implementation:
int countCombinations(int[] numbers, int target) {
// d[i][j] = n means there are n combinations of the first j numbers summing to i.
int[][] d = new int[target + 1][numbers.length + 1];
// There is always 1 combination summing to 0, namely the empty set.
for (int j = 0; j <= numbers.length; ++j) {
d[0][j] = 1;
}
// For each total i, calculate the effect of using or omitting each number j.
for (int i = 1; i <= target; ++i) {
for (int j = 1; j <= numbers.length; ++j) {
// "First j numbers" is 1-indexed, our array is 0-indexed.
int number = numbers[j - 1];
// Initialize value to 0.
d[i][j] = 0;
// How many combinations were there before considering the jth number?
d[i][j] += d[i][j - 1];
// How many things summed to i - number?
if (i - number >= 0) {
d[i][j] += d[i - number][j - 1];
}
}
}
// Return the entry in the table storing all the number combos summing to target.
return d[target][numbers.length - 1];
}
Just to add some Google keywords: this problem is also known as summing n coins without repeats to a target sum.
There is a dynamic knapsack solution for this task.In dp array dp[i] stores the number of subsets which their sum is "i". In this case your answer is dp[K].( Sorry for indentation problems I could not figure out how to make it right :( )
dp[0] = 1 ;
for( int i=0; i<N ; i++ )
for( int j=K-a[i] ; j>=0 ; j-- )
dp[j+a[i]] += dp[j]
I don't think Max's algorithm works for the case: [0,0,1] with target of 1. The answer is 4 but his algorithm will output 1. His algorithm only works for positive integers, because it assumes that a sum of 0 can only be achieved with empty set. However, it can also be achieved if 0 exists in the array. The more robust way of tackling this problem (and also more space efficient) is using a 1D dp array. The pseudo-code is the following:
int[] dp = new int[target+1];
for (int num : nums) {
for (int s = target; s >= 0; s--) {
if (s >= num) { // can include num
dp[s] += dp[s-num];
} else { // cannot include num (can be omitted, here for better explanation)
dp[s] += 0;
}
}
}
return dp[target+1];
The reason I backtrack from target to 0 in the inner for loop is to avoid duplication. Think about the example [2,2,2] with target sum of 4. If you iterate from index 0, then you would double count a 2 when you are at dp[4] (should be [1 0 1 0 0] instead of [1 0 1 0 1] after one iteration in inner loop).
Hope this helps.

Finding out the minimum difference between elements in an array

I have an integer array with some finite number of values. My job is to find the minimum difference between any two elements in the array.
Consider that the array contains
4, 9, 1, 32, 13
Here the difference is minimum between 4 and 1 and so answer is 3.
What should be the algorithm to approach this problem. Also, I don't know why but I feel that using trees, this problem can be solved relatively easier. Can that be done?
The minimum difference will be one of the differences from among the consecutive pairs in sorted order. Sort the array, and go through the pairs of adjacent numbers looking for the smallest difference:
int[] a = new int[] {4, 9, 1, 32, 13};
Arrays.sort(a);
int minDiff = a[1]-a[0];
for (int i = 2 ; i != a.length ; i++) {
minDiff = Math.min(minDiff, a[i]-a[i-1]);
}
System.out.println(minDiff);
This prints 3.
You can take advantage of the fact that you are considering integers
to make a linear algorithm:
First pass:
compute the maximum and the minimum
Second pass:
allocate a boolean array of length (max - min + 1), false initialized,
and change the (value - min)th value to true for every value in the array
Third pass:
compute the differences between the indexes of the true valued entries of the boolean array.
While all the answers are correct, I wanted to show the underlying algorithm responsible for n log n run time. The divide and conquer way of finding the minimum distance between the two points or finding the closest points in a 1-D plane.
The general algorithm:
Let m = median(S).
Divide S into S1, S2 at m.
δ1 = Closest-Pair(S1).
δ2 = Closest-Pair(S2).
δ12 is minimum distance across the cut.
Return δ = min(δ1, δ2, δ12).
Here is a sample I created in Javascript:
// Points in 1-D
var points = [4, 9, 1, 32, 13];
var smallestDiff;
function mergeSort(arr) {
if (arr.length == 1)
return arr;
if (arr.length > 1) {
let breakpoint = Math.ceil((arr.length / 2));
// Left list starts with 0, breakpoint-1
let leftList = arr.slice(0, breakpoint);
// Right list starts with breakpoint, length-1
let rightList = arr.slice(breakpoint, arr.length);
// Make a recursive call
leftList = mergeSort(leftList);
rightList = mergeSort(rightList);
var a = merge(leftList, rightList);
return a;
}
}
function merge(leftList, rightList) {
let result = [];
while (leftList.length && rightList.length) {
// Sorting the x coordinates
if (leftList[0] <= rightList[0]) {
result.push(leftList.shift());
} else {
result.push(rightList.shift());
}
}
while (leftList.length)
result.push(leftList.shift());
while (rightList.length)
result.push(rightList.shift());
let diff;
if (result.length > 1) {
diff = result[1] - result[0];
} else {
diff = result[0];
}
if (smallestDiff) {
if (diff < smallestDiff)
smallestDiff = diff;
} else {
smallestDiff = diff;
}
return result;
}
mergeSort(points);
console.log(`Smallest difference: ${smallestDiff}`);
I would put them in a heap in O(nlogn) then pop one by one and get the minimum difference between every element that I pop. Finally I would have the minimum difference. However, there might be a better solution.
This is actually a restatement of the closest-pair problem in one-dimension.
https://en.wikipedia.org/wiki/Closest_pair_of_points_problem
http://www.cs.umd.edu/~samir/grant/cp.pdf
As the Wikipedia article cited below points out, the best decision-tree model of this problem also runs in Ω(nlogn) time.
sharing the simplest solution.
function FindMin(arr) {
//sort the array in increasing order
arr.sort((a,b) => {
return a-b;
});
let min = arr[1]-arr[0];
let n = arr.length;
for (var i=0;i<n;i++) {
let m = arr[i+1] - arr[i];
if(m < min){
m = min;
}
}
return m; // minimum difference.
}
The given problem can easily be solved in O(n) time. Look at the following code that I wrote.
import java.util.Scanner;
public class Solution {
public static void main(String [] args) {
Scanner input = new Scanner(System.in);
int i, minDistance = 999999;
boolean flag = false;
int capacity = input.nextInt();
int arr[] = new int[capacity];
for (i = 0; i < capacity; i++) {
arr[i] = input.nextInt();
}
int firstElement = input.nextInt();
int secondElement = input.nextInt();
int prev = 0;
for (i = 0; i < capacity; i++) {
if (arr[i] == firstElement || arr[i] == secondElement) {
prev = i;
break;
}
}
for (; i < capacity; i++) {
if(arr[i] == firstElement || arr[i] == secondElement) {
if(arr[i] != arr[prev] && minDistance > Math.abs(i - prev)) {
minDistance = Math.abs(i - prev);
flag = true;
prev = i;
} else {
prev = i;
}
}
}
if(flag)
System.out.println(minDistance);
else
System.out.println("-1");
}
}
For those of you who are looking for a one-line python answer (more or less), these are 2 possible solutions:
Python >= 3.10
l = sorted([4, 9, 1, 32, 13])
min(map(lambda x: x[1] - x[0], pairwise(l)))
From Python 3.10 you can use pairwise() that takes an iterable and returns all consecutive pairs of it. After we sort the initial list, we just need to find the pair with the minimum difference.
Python < 3.10
l = sorted([4, 9, 1, 32, 13])
min(map(lambda x: x[1] - x[0], zip(l[:-1], l[1:])))
In this case, we can reproduce the pairwise() method behavior1 using zip() with 2 slices of the same list so that consecutive elements are paired.
1. The actual implementation of pairwise() is probably more efficient in terms of space because it doesn't need to create 2 (shallow) copies of the list. In most cases, this should not be necessary, but you can use itertools.islice to iterate over the list without creating a copy of it. Then, you would write something like zip(islice(a, len(a) - 1), islice(a, 1, None)).
In Python 3 this problem can be simplified by using the module itertools which gives the combinations available for a list. From that list we can find the sum of each combination and find the minimum of those values.
import itertools
arr = [4, 9, 1, 32, 13]
if len(arr) > 1:
min_diff = abs(arr[0] - arr[1])
else:
min_diff = 0
for n1, n2 in itertools.combinations(arr, 2): # Get the combinations of numbers
diff = abs(n1-n2) # Find the absolute difference of each combination
if min_diff > diff:
min_diff = diff # Replace incase a least differnce found
print(min_diff)

Dynamic programming exercise for string cutting

I have been working on the following problem from this book.
A certain string-processing language offers a primitive operation which splits a string into two pieces. Since this operation involves copying the original string, it takes n units of time for a string of length n, regardless of the location of the cut. Suppose, now, that you want to break a string into many pieces. The order in which the breaks are made can affect the total running time. For example, if you want to cut a 20-character string at positions 3 and 10, then making the first cut at position 3 incurs a total cost of 20+17=37, while doing position 10 first has a better cost of 20+10=30.
I need a dynamic programming algorithm that given m cuts, finds the minimum cost of cutting a string into m+1 pieces.
The divide and conquer approach seems to me the best one for this kind of problem. Here is a Java implementation of the algorithm:
Note: the array m should be sorted in ascending order (use Arrays.sort(m);)
public int findMinCutCost(int[] m, int n) {
int cost = n * m.length;
for (int i=0; i<m.length; i++) {
cost = Math.min(findMinCutCostImpl(m, n, i), cost);
}
return cost;
}
private int findMinCutCostImpl(int[] m, int n, int i) {
if (m.length == 1) return n;
int cl = 0, cr = 0;
if (i > 0) {
cl = Integer.MAX_VALUE;
int[] ml = Arrays.copyOfRange(m, 0, i);
int nl = m[i];
for (int j=0; j<ml.length; j++) {
cl = Math.min(findMinCutCostImpl(ml, nl, j), cl);
}
}
if (i < m.length - 1) {
cr = Integer.MAX_VALUE;
int[] mr = Arrays.copyOfRange(m, i + 1, m.length);
int nr = n - m[i];
for (int j=0; j<mr.length; j++) {
mr[j] = mr[j] - m[i];
}
for (int j=0; j<mr.length; j++) {
cr = Math.min(findMinCutCostImpl(mr, nr, j), cr);
}
}
return n + cl + cr;
}
For example :
int n = 20;
int[] m = new int[] { 10, 3 };
System.out.println(findMinCutCost(m, n));
Will print 30
** Edit **
I have implemented two other methods to answer the problem in the question.
1. Median cut approximation
This method cut recursively always the biggest chunks. The results are not always the best solution, but offers a not negligible gain (in the order of +100000% gain from my tests) for a negligible minimal cut loss difference from the best cost.
public int findMinCutCost2(int[] m, int n) {
if (m.length == 0) return 0;
if (m.length == 1) return n;
float half = n/2f;
int bestIndex = 0;
for (int i=1; i<m.length; i++) {
if (Math.abs(half - m[bestIndex]) > Math.abs(half - m[i])) {
bestIndex = i;
}
}
int cl = 0, cr = 0;
if (bestIndex > 0) {
int[] ml = Arrays.copyOfRange(m, 0, bestIndex);
int nl = m[bestIndex];
cl = findMinCutCost2(ml, nl);
}
if (bestIndex < m.length - 1) {
int[] mr = Arrays.copyOfRange(m, bestIndex + 1, m.length);
int nr = n - m[bestIndex];
for (int j=0; j<mr.length; j++) {
mr[j] = mr[j] - m[bestIndex];
}
cr = findMinCutCost2(mr, nr);
}
return n + cl + cr;
}
2. A constant time multi-cut
Instead of calculating the minimal cost, just use different indices and buffers. Since this method executes in a constant time, it always returns n. Plus, the method actually split the string in substrings.
public int findMinCutCost3(int[] m, int n) {
char[][] charArr = new char[m.length+1][];
charArr[0] = new char[m[0]];
for (int i=0, j=0, k=0; j<n; j++) {
//charArr[i][k++] = string[j]; // string is the actual string to split
if (i < m.length && j == m[i]) {
if (++i >= m.length) {
charArr[i] = new char[n - m[i-1]];
} else {
charArr[i] = new char[m[i] - m[i-1]];
}
k=0;
}
}
return n;
}
Note: that this last method could easily be modified to accept a String str argument instead of n and set n = str.length(), and return a String[] array from charArr[][].
For dynamic programming, I claim that all you really need to know is what the state space should be - how to represent partial problems.
Here we are dividing a string up into m+1 pieces by creating new breaks. I claim that a good state space is a set of (a, b) pairs, where a is the location of the start of a substring and b is the location of the end of the same substring, counted as number of breaks in the final broken down string. The cost associated with each pair is the minimum cost of breaking it up. If b <= a + 1, then the cost is 0, because there are no more breaks to put in. If b is larger, then the possible locations for the next break in that substring are the points a+1, a+2,... b-1. The next break is going to cost b-a regardless of where we put it, but if we put it at position k the minimum cost of later breaks is (a, k) + (k, b).
So to solve this with dynamic programming, build up a table (a, b) of minimum costs, where you can work out the cost of breaks on strings with k sections by considering k - 1 possible breaks and then looking up the costs of strings with at most k - 1 sections.
One way to expand on this would be to start by creating a table T[a, b] and setting all entries in that table to infinity. Then go over the table again and where b <= a+1 put T[a,b] = 0. This fills in entries representing sections of the original string which need no further cuts. Now scan through the table and for each T[a,b] with b > a + 1 consider every possible k such that a < k < b and if min_k ((length between breaks a and b) + T[a,k] + T[k,b]) < T[a,b] set T[a,b] to that minimum value. This recognizes where you now know a way to chop up the substrings represented by T[a,k] and T[k,b] cheaply, so this gives you a better way to chop up T[a,b]. If you now repeat this m times you are done - use a standard dynamic programming backtrack to work out the solution. It might help if you save the best value of k for each T[a,b] in a separate table.
python code:
mincost(n, cut_list) =min { n+ mincost(k,left_cut_list) + min(n-k, right_cut_list) }
import sys
def splitstr(n,cut_list):
if len(cut_list) == 0:
return [0,[]]
min_positions = []
min_cost = sys.maxint
for k in cut_list:
left_split = [ x for x in cut_list if x < k]
right_split = [ x-k for x in cut_list if x > k]
#print n,k, left_split, right_split
lcost = splitstr(k,left_split)
rcost = splitstr(n-k,right_split)
cost = n+lcost[0] + rcost[0]
positions = [k] + lcost[1]+ [x+k for x in rcost[1]]
#print "cost:", cost, " min: ", positions
if cost < min_cost:
min_cost = cost
min_positions = positions
return ( min_cost, min_positions)
print splitstr(20,[3,10,16]) # (40, [10, 3, 16])
print splitstr(20,[3,10]) # (30, [10, 3])
print splitstr(5,[1,2,3,4,5]) # (13, [2, 1, 3, 4, 5])
print splitstr(1,[1]) # (1, [1]) # m cuts m+1 substrings
Here is a c++ implementation. Its an O(n^3) Implementation using D.P . Assuming that the cut array is sorted . If it is not it takes O(n^3) time to sort it hence asymptotic time complexity remains same.
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <limits.h>
using namespace std;
int main(){
int i,j,gap,k,l,m,n;
while(scanf("%d%d",&n,&k)!=EOF){
int a[n+1][n+1];
int cut[k];
memset(a,0,sizeof(a));
for(i=0;i<k;i++)
cin >> cut[i];
for(gap=1;gap<=n;gap++){
for(i=0,j=i+gap;j<=n;j++,i++){
if(gap==1)
a[i][j]=0;
else{
int min = INT_MAX;
for(m=0;m<k;m++){
if(cut[m]<j and cut[m] >i){
int cost=(j-i)+a[i][cut[m]]+a[cut[m]][j];
if(cost<min)
min=cost;
}
}
if(min>=INT_MAX)
a[i][j]=0;
else
a[i][j]=min;
}
}
}
cout << a[0][n] << endl;
}
return 0;
}

Find 2 numbers in an unsorted array equal to a given sum

We need to find pair of numbers in an array whose sum is equal to a given value.
A = {6,4,5,7,9,1,2}
Sum = 10
Then the pairs are - {6,4} , {9,1}
I have two solutions for this .
an O(nlogn) solution - sort + check sum with 2 iterators (beginning and end).
an O(n) solution - hashing the array. Then checking if sum-hash[i] exists in the hash table or not.
But , the problem is that although the second solution is O(n) time , but uses O(n) space as well.
So , I was wondering if we could do it in O(n) time and O(1) space. And this is NOT homework!
Use in-place radix sort and OP's first solution with 2 iterators, coming towards each other.
If numbers in the array are not some sort of multi-precision numbers and are, for example, 32-bit integers, you can sort them in 2*32 passes using practically no additional space (1 bit per pass). Or 2*8 passes and 16 integer counters (4 bits per pass).
Details for the 2 iterators solution:
First iterator initially points to first element of the sorted array and advances forward. Second iterator initially points to last element of the array and advances backward.
If sum of elements, referenced by iterators, is less than the required value, advance first iterator. If it is greater than the required value, advance second iterator. If it is equal to the required value, success.
Only one pass is needed, so time complexity is O(n). Space complexity is O(1). If radix sort is used, complexities of the whole algorithm are the same.
If you are interested in related problems (with sum of more than 2 numbers), see "Sum-subset with a fixed subset size" and "Finding three elements in an array whose sum is closest to an given number".
This is a classic interview question from Microsoft research Asia.
How to Find 2 numbers in an unsorted array equal to a given sum.
[1]brute force solution
This algorithm is very simple. The time complexity is O(N^2)
[2]Using binary search
Using bianry searching to find the Sum-arr[i] with every arr[i], The time complexity can be reduced to O(N*logN)
[3]Using Hash
Base on [2] algorithm and use hash, the time complexity can be reduced to O(N), but this solution will add the O(N) space of hash.
[4]Optimal algorithm:
Pseduo-code:
for(i=0;j=n-1;i<j)
if(arr[i]+arr[j]==sum) return (i,j);
else if(arr[i]+arr[j]<sum) i++;
else j--;
return(-1,-1);
or
If a[M] + a[m] > I then M--
If a[M] + a[m] < I then m++
If a[M] + a[m] == I you have found it
If m > M, no such numbers exist.
And, Is this quesiton completely solved? No. If the number is N. This problem will become very complex.
The quesiton then:
How can I find all the combination cases with a given number?
This is a classic NP-Complete problem which is called subset-sum.
To understand NP/NPC/NP-Hard you'd better to read some professional books.
References:
[1]http://www.quora.com/Mathematics/How-can-I-find-all-the-combination-cases-with-a-given-number
[2]http://en.wikipedia.org/wiki/Subset_sum_problem
for (int i=0; i < array.size(); i++){
int value = array[i];
int diff = sum - value;
if (! hashSet.contains(diffvalue)){
hashSet.put(value,value);
} else{
printf(sum = diffvalue + hashSet.get(diffvalue));
}
}
--------
Sum being sum of 2 numbers.
public void printPairsOfNumbers(int[] a, int sum){
//O(n2)
for (int i = 0; i < a.length; i++) {
for (int j = i+1; j < a.length; j++) {
if(sum - a[i] == a[j]){
//match..
System.out.println(a[i]+","+a[j]);
}
}
}
//O(n) time and O(n) space
Set<Integer> cache = new HashSet<Integer>();
cache.add(a[0]);
for (int i = 1; i < a.length; i++) {
if(cache.contains(sum - a[i])){
//match//
System.out.println(a[i]+","+(sum-a[i]));
}else{
cache.add(a[i]);
}
}
}
Create a dictionary with pairs Key (number from the list) and the Value is the number which is necessary to obtain a desired value. Next, check the presence of the pairs of numbers in the list.
def check_sum_in_list(p_list, p_check_sum):
l_dict = {i: (p_check_sum - i) for i in p_list}
for key, value in l_dict.items():
if key in p_list and value in p_list:
return True
return False
if __name__ == '__main__':
l1 = [1, 3, 7, 12, 72, 2, 8]
l2 = [1, 2, 2, 4, 7, 4, 13, 32]
print(check_sum_in_list(l1, 10))
print(check_sum_in_list(l2, 99))
Output:
True
Flase
version 2
import random
def check_sum_in_list(p_list, p_searched_sum):
print(list(p_list))
l_dict = {i: p_searched_sum - i for i in set(p_list)}
for key, value in l_dict.items():
if key in p_list and value in p_list:
if p_list.index(key) != p_list.index(value):
print(key, value)
return True
return False
if __name__ == '__main__':
l1 = []
for i in range(1, 2000000):
l1.append(random.randrange(1, 1000))
j = 0
i = 9
while i < len(l1):
if check_sum_in_list(l1[j:i], 100):
print('Found')
break
else:
print('Continue searching')
j = i
i = i + 10
Output:
...
[154, 596, 758, 924, 797, 379, 731, 278, 992, 167]
Continue searching
[808, 730, 216, 15, 261, 149, 65, 386, 670, 770]
Continue searching
[961, 632, 39, 888, 61, 18, 166, 167, 474, 108]
39 61
Finded
[Finished in 3.9s]
If you assume that the value M to which the pairs are suppose to sum is constant and that the entries in the array are positive, then you can do this in one pass (O(n) time) using M/2 pointers (O(1) space) as follows. The pointers are labeled P1,P2,...,Pk where k=floor(M/2). Then do something like this
for (int i=0; i<N; ++i) {
int j = array[i];
if (j < M/2) {
if (Pj == 0)
Pj = -(i+1); // found smaller unpaired
else if (Pj > 0)
print(Pj-1,i); // found a pair
Pj = 0;
} else
if (Pj == 0)
Pj = (i+1); // found larger unpaired
else if (Pj < 0)
print(Pj-1,i); // found a pair
Pj = 0;
}
}
You can handle repeated entries (e.g. two 6's) by storing the indices as digits in base N, for example. For M/2, you can add the conditional
if (j == M/2) {
if (Pj == 0)
Pj = i+1; // found unpaired middle
else
print(Pj-1,i); // found a pair
Pj = 0;
}
But now you have the problem of putting the pairs together.
Does the obvious solution not work (iterating over every consecutive pair) or are the two numbers in any order?
In that case, you could sort the list of numbers and use random sampling to partition the sorted list until you have a sublist that is small enough to be iterated over.
public static ArrayList<Integer> find(int[] A , int target){
HashSet<Integer> set = new HashSet<Integer>();
ArrayList<Integer> list = new ArrayList<Integer>();
int diffrence = 0;
for(Integer i : A){
set.add(i);
}
for(int i = 0; i <A.length; i++){
diffrence = target- A[i];
if(set.contains(diffrence)&&A[i]!=diffrence){
list.add(A[i]);
list.add(diffrence);
return list;
}
}
return null;
}
`package algorithmsDesignAnalysis;
public class USELESStemp {
public static void main(String[] args){
int A[] = {6, 8, 7, 5, 3, 11, 10};
int sum = 12;
int[] B = new int[A.length];
int Max =A.length;
for(int i=0; i<A.length; i++){
B[i] = sum - A[i];
if(B[i] > Max)
Max = B[i];
if(A[i] > Max)
Max = A[i];
System.out.print(" " + B[i] + "");
} // O(n) here;
System.out.println("\n Max = " + Max);
int[] Array = new int[Max+1];
for(int i=0; i<B.length; i++){
Array[B[i]] = B[i];
} // O(n) here;
for(int i=0; i<A.length; i++){
if (Array[A[i]] >= 0)
System.out.println("We got one: " + A[i] +" and " + (sum-A[i]));
} // O(n) here;
} // end main();
/******
Running time: 3*O(n)
*******/
}
Below code takes the array and the number N as the target sum.
First the array is sorted, then a new array containing the
remaining elements are taken and then scanned not by binary search
but simple scanning of the remainder and the array simultaneously.
public static int solution(int[] a, int N) {
quickSort(a, 0, a.length-1); // nlog(n)
int[] remainders = new int[a.length];
for (int i=0; i<a.length; i++) {
remainders[a.length-1-i] = N - a[i]; // n
}
int previous = 0;
for (int j=0; j<a.length; j++) { // ~~ n
int k = previous;
while(k < remainders.length && remainders[k] < a[j]) {
k++;
}
if(k < remainders.length && remainders[k] == a[j]) {
return 1;
}
previous = k;
}
return 0;
}
Shouldn't iterating from both ends just solve the problem?
Sort the array. And start comparing from both ends.
if((arr[start] + arr[end]) < sum) start++;
if((arr[start] + arr[end]) > sum) end--;
if((arr[start] + arr[end]) = sum) {print arr[start] "," arr[end] ; start++}
if(start > end) break;
Time Complexity O(nlogn)
if its a sorted array and we need only pair of numbers and not all the pairs we can do it like this:
public void sums(int a[] , int x){ // A = 1,2,3,9,11,20 x=11
int i=0 , j=a.length-1;
while(i < j){
if(a[i] + a[j] == x) system.out.println("the numbers : "a[x] + " " + a[y]);
else if(a[i] + a[j] < x) i++;
else j--;
}
}
1 2 3 9 11 20 || i=0 , j=5 sum=21 x=11
1 2 3 9 11 20 || i=0 , j=4 sum=13 x=11
1 2 3 9 11 20 || i=0 , j=4 sum=11 x=11
END
The following code returns true if two integers in an array match a compared integer.
function compareArraySums(array, compare){
var candidates = [];
function compareAdditions(element, index, array){
if(element <= y){
candidates.push(element);
}
}
array.forEach(compareAdditions);
for(var i = 0; i < candidates.length; i++){
for(var j = 0; j < candidates.length; j++){
if (i + j === y){
return true;
}
}
}
}
Python 2.7 Implementation:
import itertools
list = [1, 1, 2, 3, 4, 5,]
uniquelist = set(list)
targetsum = 5
for n in itertools.combinations(uniquelist, 2):
if n[0] + n[1] == targetsum:
print str(n[0]) + " + " + str(n[1])
Output:
1 + 4
2 + 3
https://github.com/clockzhong/findSumPairNumber
#! /usr/bin/env python
import sys
import os
import re
#get the number list
numberListStr=raw_input("Please input your number list (seperated by spaces)...\n")
numberList=[int(i) for i in numberListStr.split()]
print 'you have input the following number list:'
print numberList
#get the sum target value
sumTargetStr=raw_input("Please input your target number:\n")
sumTarget=int(sumTargetStr)
print 'your target is: '
print sumTarget
def generatePairsWith2IndexLists(list1, list2):
result=[]
for item1 in list1:
for item2 in list2:
#result.append([item1, item2])
result.append([item1+1, item2+1])
#print result
return result
def generatePairsWithOneIndexLists(list1):
result=[]
index = 0
while index< (len(list1)-1):
index2=index+1
while index2 < len(list1):
#result.append([list1[index],list1[index2]])
result.append([list1[index]+1,list1[index2]+1])
index2+=1
index+=1
return result
def getPairs(numList, target):
pairList=[]
candidateSlots=[] ##we have (target-1) slots
#init the candidateSlots list
index=0
while index < target+1:
candidateSlots.append(None)
index+=1
#generate the candidateSlots, contribute O(n) complexity
index=0
while index<len(numList):
if numList[index]<=target and numList[index]>=0:
#print 'index:',index
#print 'numList[index]:',numList[index]
#print 'len(candidateSlots):',len(candidateSlots)
if candidateSlots[numList[index]]==None:
candidateSlots[numList[index]]=[index]
else:
candidateSlots[numList[index]].append(index)
index+=1
#print candidateSlots
#generate the pairs list based on the candidateSlots[] we just created
#contribute O(target) complexity
index=0
while index<=(target/2):
if candidateSlots[index]!=None and candidateSlots[target-index]!=None:
if index!=(target-index):
newPairList=generatePairsWith2IndexLists(candidateSlots[index], candidateSlots[target-index])
else:
newPairList=generatePairsWithOneIndexLists(candidateSlots[index])
pairList+=newPairList
index+=1
return pairList
print getPairs(numberList, sumTarget)
I've successfully implemented one solution with Python under O(n+m) time and space cost.
The "m" means the target value which those two numbers' sum need equal to.
I believe this is the lowest cost could get. Erict2k used itertools.combinations, it'll also cost similar or higher time&space cost comparing my algorithm.
If numbers aren't very big, you can use fast fourier transform to multiply two polynomials and then in O(1) check if coefficient before x^(needed sum) sum is more than zero. O(n log n) total!
// Java implementation using Hashing
import java.io.*;
class PairSum
{
private static final int MAX = 100000; // Max size of Hashmap
static void printpairs(int arr[],int sum)
{
// Declares and initializes the whole array as false
boolean[] binmap = new boolean[MAX];
for (int i=0; i<arr.length; ++i)
{
int temp = sum-arr[i];
// checking for condition
if (temp>=0 && binmap[temp])
{
System.out.println("Pair with given sum " +
sum + " is (" + arr[i] +
", "+temp+")");
}
binmap[arr[i]] = true;
}
}
// Main to test the above function
public static void main (String[] args)
{
int A[] = {1, 4, 45, 6, 10, 8};
int n = 16;
printpairs(A, n);
}
}
public static void Main(string[] args)
{
int[] myArray = {1,2,3,4,5,6,1,4,2,2,7 };
int Sum = 9;
for (int j = 1; j < myArray.Length; j++)
{
if (myArray[j-1]+myArray[j]==Sum)
{
Console.WriteLine("{0}, {1}",myArray[j-1],myArray[j]);
}
}
Console.ReadLine();
}

Resources