This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Union of intervals
how to overlap intervals efficiently
Given a list of intervals say all integers, it should be possible to collapse them into one interval if they do intersect or overlap otherwise the given intervals remain unaffected.
Say, if the input is e.g.I [(2-6), (1-4), (8-12)], the expected output is [(1-6), (8-12)]
e.g. II [(4-7), (2-6), (1-4), (8-12), (7-9)] the expected output is [(1-12)].
Correction: Missed the sorting part, so yes, it is a O(nlogn) time NOT O(n). Thanks for pointing that out.
I have written and tested a O(nlogn) time and O(2n) space algorithm approach that works. Sharing the code of this approach below. I am interested in hearing different approaches to solving this problem, possibly more efficient.
//Assuming each of the intervals, (2-6) and so on are represented as "Interval" objects (class definition shown below), where low = 2 and high = 6
// Step1: Sort by the low endpoint of the given intervals
// Step2: find Union of the sorted intervals
//Input:
List<Interval> intervalList = new ArrayList<Interval>();
//Output:
List<Interval> unionList = new ArrayList<Interval>();
private static final Comparator<Interval> Low_EPSorter = new LowEPSorter();
class Interval {
int low, high;
Interval(int l, int h, int m) {
low = l;
high = h;
}
}
////-------BEGIN: Method which finds the Union Of given intervals ----//////
void UnionOfIntervals() {
//Find intersection and combine intervals as necessary
int sz = intervalList.size();
// sort by low endpoint
Collections.sort(intervalList, Low_EPSorter);
for(int i = 0; i < sz; i++) {
int j = i;
if(j > 0) {
if( Intervals.intersect(intervalList.get(j), intervalList.get(j-1)) ) {
Interval v = union(intervalList.get(j), intervalList.get(j-1));
checkAndAdd(v, unionList);
}
else {
if(i == 1) {
unionList.add(intervalList.get(j-1));
unionList.add(intervalList.get(j));
}
else {
unionList.add(intervalList.get(j));
}
} //No intersection
} //If 2 elements atleast
}
//Print intervals after union
System.out.println("Input intervals after sorting:");
for(Interval v : intervalList) {
System.out.print(v.low + "," + v.high + " ");
}
System.out.println();
System.out.println("Union of intervals:");
for(Interval v : unionList) {
System.out.print(v.low + "," + v.high + " ");
}
}
void checkAndAdd(Interval x, List t) {
int top = t.size()-1;
if( top >=0 && Intervals.intersect(unionList.get(top), x) ) {
Interval v = union(unionList.get(top), x);
t.remove(top);
t.add(v);
}
else {
t.add(x);
}
}
////-------END: Method which finds the Union Of given intervals ----//////
////--- helper methods --- ////
static boolean intersect(Interval a, Interval b) {
boolean r = false;
if(b.high < a.low || b.low > a.high)
r = false;
else if(a.low <= b.high && b.low <= a.high)
r = true;
return r;
}
Interval union(Interval a, Interval b) {
int l = (a.low < b.low) ? a.low : b.low;
int max = (a.high > b.high) ? a.high : b.high;
return new Interval(l, max);
}
private static class LowEPSorter implements Comparator<Interval> {
public int compare(Interval a, Interval b) {
int r = 0;
if(a.low < b.low)
r = -1;
else if(a.low > b.low)
r = 1;
return r;
}
}
Take a array of size n ( where n is the largest number ) and fills the interval start and end with 1 and -1 respectively.
By this I mean if the intervals are
{[1-4],[6-8]}
Then the array elements would be like
array[1]=1,array[4]=-1,array[6]=1,array[8]=-1
and the rest all other locations of array would be set to zero.
Now traverse the array and by scanning the array we can get the intervals, like for the case
{[1-4],[2-5],[7-9]},
first fill the array as stated above , the array A would look like(assuming the starting index be 1):
A=[1,1,0,-1,-1,0,1,0,1]
Now traverse the array A from beginning and take a variable sum=0 and add the value stored at location of array to sum.
Stating the sum at each index of array:
At location 1: sum = 1 ( 1 at index 1 )
At location 2: sum = 2 ( 1 at index 2 )
At location 3: sum = 2 ( 0 at index 3 )
At location 4: sum = 1 ( -1 at index 4 )
At location 5: sum = 0 ( -1 at index 5 )
Now the sum reaches to zero it means an interval ends here so the new interval will be [1-5]
At location 6: sum = 0 ( 0 at index 6 )
At location 7: sum = 1 ( 1 at index 7 )
(At location 7 the sum again becomes greater than zero it means an interval has just started)
At location 8: sum = 1 ( 0 at index 8 )
At location 9: sum = 0 ( -1 at index 9 )
Interval started at location 7 just ended so the new interval ranges would be
{[1-5],[7-9]}
Hope it helps.
If you are looking for a more efficient algorithm than O(n) for this problem, I don't believe that you will find it. No matter what data structure you use for storing the initial interval values, your worst case scenario is that none of the intervals overlap and that you have to check each interval to confirm this, hence the O(n). Even with a HashMap and a very elaborate key structure, you are still looking at O(n).
With this being said, I'm not sure if any other approach is worth investigating as you have already found an algorithm that solves it in the best time possible, O(n).
Just an idea: manage a set of non-intersecting intervals. Starting from empty set, add incoming intervals. If new interval intersects with one or two existing intervals, combine them. To calculate intersections, use 2 TreeMaps referencing to the same set of intervals but by different keys: low and high bounds.
Just another idea:
Use a Bool array
make all the values false by default
for all the intervals(in input) make the values true
Scan the updated array and get the final intersected result.
Related
tl;dr
My recurrence relation is accounting for a smaller number of graphs than should be.
I need to find the number of simple connected graphs with N labeled vertices and K unlabeled edges. Link to full source with complete question
[I have seen this post and it didn't solve my question]
Constraints: 2 <= N <= 20. It follows that, N-1 <= K <= N(N-1)/2.
I approached this problem with two different (not quite, I later realized) ideas.
The first idea: Connect N nodes with K edges such that there is 1 path between 2 nodes
Ideation: Consider N-1 nodes and K-1 edges. How many ways to add Nth node?
distribute 1 edge between node N and any of the other N-1 nodes;
this is trivial, \binom {N-1}1, i.e., given N-1 choose 1.
distribute 2 edges between ....
....
....
distribute N-1 edges between ....
The 'formula' I came up with looked something like this:
We only look at values of K ∈ [N-1, N(N-1)/2] (other values don't make sense). When K = N-1, it's essentially falls under Cayley's formula. The recurrence relation is the part I came up with. The issue is that I am accounting for a smaller number of graphs than should be. The code:
static Map<List<Integer>, String> resultMap = new HashMap<List<Integer>, String>();
// N -> number of nodes
// K -> number of edges
// N will be at least 2 and at most 20.
// K will be at least one less than n and at most (n * (n - 1)) / 2
public static String answer(int N, int K) {
/* for the case where K < N-1 */
if(K < N-1)
return BigInteger.ZERO.toString();
/* for the case where K = N-1 */
// Cayley's formula applies [https://en.wikipedia.org/wiki/Cayley's_formula].
// number of trees on n labeled vertices is n^{n-2}.
if(K == N-1)
return BigInteger.valueOf((long)Math.pow(N, N-2)).toString();
/* for the case where K > N-1 */
// check if key is present in the map
List<Integer> tuple = Arrays.asList(N, K);
if( resultMap.containsKey(tuple) )
return resultMap.get(tuple);
// maximum number of edges in a simply
// connected undirected unweighted graph
// with n nodes = |N| * |N-1| / 2
int maxEdges = N * (N-1) / 2;
/* for the case where K = N(N-1)/2 */
// if K is the maximum possible
// number of edges for the number of
// nodes, then there is only one way is
// to make a graph (connect each node
// to all other nodes)
if(K == maxEdges)
return BigInteger.ONE.toString();
/* for the case where K > N(N-1)/2 */
if(K > maxEdges)
return BigInteger.ZERO.toString();
BigInteger count = BigInteger.ZERO;
for(int k = 1; k <= N-1 ; k++) {
BigInteger combinations = nChooseR(N-1, k);
combinations = combinations.multiply(new BigInteger(answer(N-1, K-k)));
count = count.add(combinations);
}
// unmodifiable so key cannot change hash code
resultMap.put(Collections.unmodifiableList(Arrays.asList(N, K)), count.toString());
return count.toString();
}
I found this post on MSE that addresses the same problem. Using that as reference, the 'formula' looked somewhat like this:
This works exactly as expected. The code for this section is below.
static Map<List<Integer>, String> resultMap2 = new HashMap<List<Integer>, String>();
// reference: https://math.stackexchange.com/questions/689526/how-many-connected-graphs-over-v-vertices-and-e-edges
public static String answer2(int N, int K) {
/* for the case where K < N-1 */
if(K < N-1)
return BigInteger.ZERO.toString();
/* for the case where K = N-1 */
// Cayley's formula applies [https://en.wikipedia.org/wiki/Cayley's_formula].
// number of trees on n labeled vertices is n^{n-2}.
if(K == N-1)
return BigInteger.valueOf((long)Math.pow(N, N-2)).toString();
/* for the case where K > N-1 */
// check if key is present in the map
List<Integer> tuple = Arrays.asList(N, K);
if( resultMap2.containsKey(tuple) )
return resultMap2.get(tuple);
// maximum number of edges in a simply
// connected undirected unweighted graph
// with n nodes = |N| * |N-1| / 2
int maxEdges = N * (N-1) / 2;
/* for the case where K = N(N-1)/2 */
// if K is the maximum possible
// number of edges for the number of
// nodes, then there is only one way is
// to make a graph (connect each node
// to all other nodes)
if(K == maxEdges)
return BigInteger.ONE.toString();
/* for the case where K > N(N-1)/2 */
if(K > maxEdges)
return BigInteger.ZERO.toString();
// get the universal set
BigInteger allPossible = nChooseR(maxEdges, K);
BigInteger repeats = BigInteger.ZERO;
// now, to remove duplicates, or incomplete graphs
// when can these cases occur?
for(int n = 0 ; n <= N-2 ; n++) {
BigInteger choose_n_from_rem_nodes = nChooseR(N-1, n);
int chooseN = (N - 1 - n) * (N - 2 - n) / 2;
BigInteger repeatedEdges = BigInteger.ZERO;
for(int k = 0 ; k <= K ; k++) {
BigInteger combinations = nChooseR(chooseN, k);
BigInteger recurse = new BigInteger(answer2(n+1, K-k));
repeatedEdges = repeatedEdges.add(combinations.multiply(recurse));
}
repeats = repeats.add(choose_n_from_rem_nodes.multiply(repeatedEdges));
}
// remove repeats
allPossible = allPossible.subtract(repeats);
// add to cache
resultMap2.put(Collections.unmodifiableList(Arrays.asList(N, K)), allPossible.toString());
return resultMap2.get(tuple);
}
I would be grateful if someone could point me in a direction so that I can get the error in my first approach. The second approach works, but it makes O(NK) recursive calls and K is on average quadratic in N. So, clearly not very good, although I have tried to minimize computations using DP. The nChooseR() and factorial() functions are below.
Code for nChooseR:
static Map<List<Integer>, BigInteger> nCrMap = new HashMap<List<Integer>, BigInteger>();
// formula: nCr = n! / [r! * (n-r)!]
private static BigInteger nChooseR(int n, int r) {
// check if key is present
List<Integer> tuple = Arrays.asList(n, r);
if( nCrMap.containsKey(tuple) )
return nCrMap.get(tuple);
// covering some basic cases using
// if statements to prevent unnecessary
// calculations and memory wastage
// given 5 objects, there are 0 ways to choose 6
if(r > n)
return BigInteger.valueOf(0);
// given 5 objects, there are 5 ways of choosing 1
// given 5 objects, there are 5 ways of choosing 4
if( (r == 1) || ( (n-r) == 1 ) )
return BigInteger.valueOf(n);
// given 5 objects, there is 1 way of choosing 5 objects
// given 5 objects, there is 1 way of choosing 0 objects
if( (r == 0) || ( (n-r) == 0 ) )
return BigInteger.valueOf(1);
BigInteger diff = getFactorial(n-r);
BigInteger numerator = getFactorial(n);
BigInteger denominator = getFactorial(r);
denominator = denominator.multiply(diff);
// unmodifiable so key cannot change hash code
nCrMap.put(Collections.unmodifiableList(Arrays.asList(n, r)), numerator.divide(denominator));
return nCrMap.get(tuple);
}
Code for factorial:
private static Map<Integer, BigInteger> factorials = new HashMap<Integer, BigInteger>();
private static BigInteger getFactorial(int n) {
if(factorials.containsKey(n))
return factorials.get(n);
BigInteger fact = BigInteger.ONE;
for(int i = 2 ; i <= n ; i++)
fact = fact.multiply(BigInteger.valueOf(i));
factorials.put(n, fact);
return fact;
}
Some test code:
public static void main(String[] args) {
int fail = 0;
int total = 0;
for(int n = 2 ; n <= 20 ; n++) {
for(int k = n-1 ; k <= n*(n-1)/2 ; k++) {
total++;
String ans = answer(n,k);
String ans2 = answer2(n,k);
if(ans.compareTo(ans2) != 0) {
fail++;
System.out.println("N = " + n + " , K = " + k + " , num = " + ans + " ||| " + ans2);
}
}
}
System.out.println("Approach 1 fails " + ((100*fail)/total) + "% of the test");
}
P.S. I got this challenge as a part of the Google Foobar challenges. Just wanted to make that aware to all. answer2() was judged to be working based on the test-cases on Foobar that cannot be seen by the challenge-taker.
And just for reading all that, here is a video of a tiny hamster eating a tiny burrito.
An alternate approach...
We know that f(n,n-1) = n^{n-2} is the counting function of the number of
labeled rooted trees [Cayley’s formula]
Now, let f(n, k) be the total number of connected graphs with n nodes and k edges,
we have a characterization of how to add a new edge:
1) Take any graph in F[n,k], and you can add an edge between any of
the {n \choose 2} - k pairs of unmatched nodes.
2) If you have two connected graphs g_1 and g_2, say in F[s, t] and
F[n-s, k-t] respectively (that is, a connected graph with s nodes
and t edges and a connected graph with n-s nodes and k-t edges),
then you can surgically construct a new graph by connecting these
two subgraphs together.
You have s * (n-s) pairs of vertices to choose from, and you can choose
the s point in {n \choose s} ways. You can then sum over the choice of
s and t respectively from 1 to n-1, and in doing so, you will have
double-counted each graph twice. Let's call this construction g(n, k).
Then g(n,k) = (\sum_s,t {n \choose s} s (n-s) f(s,t) f(n-s, k-t))/2
Now, there are no other ways to add in an extra edge (without reducing to
the two constructions above), so the additive term
h(n,k+1) = (N - k)f(n,k) + g(n,k)
gives a characterization of the multiset of graphs that we've
constructed. Why is this a multiset?
Well, let's look at a case analysis on the two subcases
(induction on the construction). Take a random graph g in h(n, k+1) graphs
constructed this way. The induction hypothesis is that there are
k + 1 copies of g within the multiset h(n, k+1).
Let's just look at the inductive case
If you break an edge within a connected graph, then it either remains a connected
graph or it breaks into two connected graphs.
Now, fixate on an edge e, if you break any other edges, then e will still be
within (k+1) - 1 distinct constructions. If you break e, you will have yet
another unique construction.
This means that there are k + 1 possible distinct classes of graphs
(either single component of two components) from which we can construct the same
final graph g.
Therefore, h(n,k+1) counts each graph a total of k+1 times, and so
f(n, k+1) = h(n, k+1)/(k+1) = ((N-k)f(n,k) + g(n,k))/(k+1).
Given a fixed n and k, this recurrence will compute the correct result in O((nk)^2) time,
so complexity wise, it's equivalent to the previous algorithm.
The nice thing about this construction is that it easily yields an analytic
generating function so you can do analysis on it.
In this case, suppose you have a complex-valued function f_k(x,y),
then
2 dy f_{k+1} = (x^2 dx^2 f_k - 2 y dy f_k) + \sum_s z^2 dz f_s dz f_{k-s}.
You'll need a lot of complex analysis machinery to solve this recurrence PDE.
Here's a java implementation [source]
As the title suggests, I need to solve this puzzle.
5
9 6
4 6 8
0 7 1 5
The path I need to find is the max sum from top to bottom, only moving to adjacent children. So this path would be 5-9-6-7, with a sum of 27.
My code works for every set of data I input myself, but when I attempt the puzzles with the provided textFile's data, my sum/answer is not accepted as correct.
I cannot for the life of me figure out what is wrong with my code. Is there some exception I am not seeing?
public class Triangle
{
public static void main(String[] args) throws IOException
{
File file = new File("Tri.txt");
byte[] bytes = new byte[(int) file.length()];
try{
//Read the file and add all integers into an array with the correct size. Array size is found with number of bytes file.length()
//Parse string to integer
FileInputStream fis = new FileInputStream(file);
fis.read(bytes);
fis.close();
String[] valueStr = new String(bytes).trim().split("\\s+");
int[] list = new int[valueStr.length];
for (int i = 0; i < valueStr.length; i++)
list[i] = Integer.parseInt(valueStr[i]);
System.out.println(computeMaxPath(list));
}
catch(Exception e)
{
e.printStackTrace();
}
}
static int computeMaxPath(int[] list){
//Disregard row number one since it is the root. Start row number count at 2
int rowNumber = 2;
//set the sum to the value of the root.
int sum = list[0];
//selected index begins at the root, index 0
int selectedIndex = 0;
for (int j = 1; j < list.length; j=j+rowNumber)
{
// for every iteration the right child is found by adding the current selected index by z. What is z?
// the left child is of course found in the index -1 of the right child.
// z is the amount of of elements in the triangle's row. Row 3 has 3 elements, 4 has 4, etc.
// For exmaple, if the selectedIndex is index 4, its right child can be found by adding the index to the next row element count.
// 4 + 4 = 8 the right child is in index 8 and left is in index 7
int rightChildIndex = selectedIndex + rowNumber;
int leftChildIndex = selectedIndex + rowNumber - 1;
//set the appropriate index for the greater child's index
selectedIndex = list[rightChildIndex] >= list[leftChildIndex] ? rightChildIndex : leftChildIndex;
//increment the sum of the path
sum = sum + list[selectedIndex];
System.out.println(selectedIndex);
//increment the row number
rowNumber++;
}
return sum;
}
}
Essentially, my algorithm works by adding the string of ints from the text file into an array. The first selected index is of course the root node. To find the right child I add the selected index by the next row's length and subtract by 1 to find the left child index.
Any ideas?
This algorithm uses the wrong logic. In this case your algorithm works because it has the required properties to make your algorithm work, for other inputs this obviously not the case. For example consider the following (extreme) example:
1
1 0
0 0 9
Your algorithm works by simply always selecting the child with the larger sum, so in this case your algorithm would result in the path {1 , 1 , 0}, while the correct algorithm would result in {1 , 0 , 9}.
The correct algorithm would require to traverse the tree and search all paths in order to find the correct solution:
int findSum(int[] tree , int at_node){
if(at_node >= length(tree))
return 0 //end of the tree, quit recursive search
//maximum-path including node is the path with the greatest sum that includes either the left or right child of the node.
return max(findSum(tree , leftChild(at_node)) ,
findSum(tree , rightChild(at_node)) + tree[at_node]
}
As #JohnBollinger mentioned:
This top-to-bottom-approach is pretty simple. But on cost of efficiency. A more efficient, but also more efficient solution that only traverses each node exactly once. In the above stated algorithm a tree that represents the time each node was visited would look like a pascal's triangle, thus making 2 ^ height array-lookups. The bottom-top approach would only require height + height - 1 + ... + 1 lookups.
int findSumBottomTop(int[] tree , int height){
//initialize counter for previous level
int[] sums = new int[height + 1]
fill(sums , 0)
//counter for the level counts down to 1 (note that this variable is not 0-based!!!)
int lvl = height
//counter for nodes remaining on the current level (0-based)
int remaining_in_lvl = lvl - 1
//maximum-paths for each node on the current level
int[] next_level = new int[lvl]
//iterate over all nodes of the tree
for(int node = length(tree) - 1; node > -1 ; node--){
int left_max_path = sums[remaining_in_lvl]
int right_max_path = sums[remaining_in_lvl + 1]
next_level[remaining_in_lvl] = max(right_max_path , left_max_path) + tree[node]
//decrement counter for remaining nodes
remaining_in_lvl -= 1
if(remaining_in_lvl == -1){
//end of a level was encountered --> continue with lvl = lvl - 1
lvl--
//update to match length of next
remaining_in_lvl = lvl - 1
//setup maximum-path counters for next level
sums = next_level
next_level = new int[sums.length - 1]
}
//there is exactly one sum remaining, which is the sum of the maximum-path
return sums[0];
}
The basic idea of this would be the following:
Consider this example tree:
0 ^ 6
0 1 | 3 6
0 1 2 | 1 3 5
0 1 2 3 | 0 1 2 3
0 0 0 0 0
tree traversal sums
sums would be the values of sums that would be produced for each level. We simply start searching at the bottom and searching the maximum-path from each node in a level to the bottom. This would be the maximum of the maximum-path of the left child and the maximum-path of the right child + the value of the node.
if there is not limit on the number of rows, for example, input can have hundred of rows. it worth to implement this like a directed acyclic graph and then use an algorithm to find the largest path
Try this.
static int computeMaxPath(int[] a, int self, int row) {
if (self >= a.length)
return 0;
else
return a[self] + Math.max(
computeMaxPath(a, self + row + 1, row + 1),
computeMaxPath(a, self + row + 2, row + 1));
}
static int computeMaxPath(int[] a) {
return computeMaxPath(a, 0, 0);
}
This is one of my favorite Project Euler problems (#18). Just for reference, here's a complete bottom-to-top solution in the Haskell language:
f = foldr (\a b -> let c = zipWith (+) a b
in if null (drop 1 c)
then c
else zipWith max c (tail c)) (repeat 0)
main = print (f z) where
z = map (map read . words) (lines s) :: [[Int]]
Can anyone tell me the complexity (Big O notation preferred) of this code? It finds the least number of "coins" needed to make a target sum.
To do this it calculates the least number of coins for each number up to the target starting from 1. Each number is worked out based on the possible pairs of numbers that could sum to it, and the pair with the smallest cost is used. An example hopefully makes this clearer
If the "coins" are {1, 3, 4} and the target is 13 then it iterates from 1 to 13, where the cost of 2 the minimum from (0+2, 1+1), the c(5) is the smallest cost of (c(0)+c(5), c(1)+c(4), c(2)+c(3)), etc up to c(13)
This is a version of the knapsack problem and I'm wondering how to define its complexity?
Code:
import java.util.*;
public class coinSumMinimalistic {
public static final int TARGET = 12003;
public static int[] validCoins = {1, 3, 5, 6, 7, 10, 12};
public static void main(String[] args) {
Arrays.sort(validCoins);
sack();
}
public static void sack() {
Map<Integer, Integer> coins = new TreeMap<Integer, Integer>();
coins.put(0, 0);
int a = 0;
for(int i = 1; i <= TARGET; i++) {
if(a < validCoins.length && i == validCoins[a]) {
coins.put(i, 1);
a++;
} else coins.put(i, -1);
}
for(int x = 2; x <= TARGET; x++) {
if(x % 5000 == 0) System.out.println("AT: " + x);
ArrayList<Integer> list = new ArrayList<Integer>();
for(int i = 0; i <= x / 2; i++) {
int j = x - i;
list.add(i);
list.add(j);
}
coins.put(x, min(list, coins));
}
System.out.println("It takes " + coins.get(TARGET) + " coins to reach the target of " + TARGET);
}
public static int min(ArrayList<Integer> combos, Map<Integer, Integer> coins) {
int min = Integer.MAX_VALUE;
int total = 0;
for(int i = 0; i < combos.size() - 1; i += 2) {
int x = coins.get(combos.get(i));
int y = coins.get(combos.get(i + 1));
if(x < 0 || y < 0) continue;
else {
total = x + y;
if(total > 0 && total < min) {
min = total;
}
}
}
int t = (min == Integer.MAX_VALUE || min < 0) ? -1:min;
return t;
}
}
EDIT: Upon research I think that the complexity is O(k*n^2) where n is the target, and k is the number of coins supplied, is this correct?
I thinky the code you provided is kind of chaotic. So this post is more about the conceptual algorithm instead of the real algorithm. This can differ a bit since for instance insertion in an ArrayList<T> is not O(1), but I'm confident that you can use good datastructures (for instance LinkedList<T>s) for this to let all operations run in constant time.
What your algorithm basically does is the following:
It starts with a map that maps all the given coins to one: it requires one coin to achieve the value on the coin.
For each iteration, it mixes all already achieved values with all already achieved values. The result is thus the sum of the coins and it takes at the sum of the number of coins unless it was already present in the collection.
This step you forgot: kick out values strictly larger than the requested value: since all coins are strictly positive, you will never be able to construct a value with such composition less than the requested value.
You keep doing this until you have constructed the requested coin value.
If at iteration i all new values added to the set are strictly larger than the requested value, you can stop: the requested value can't be constructed.
The parameters are:
n: the number of coins.
r: the requested value.
A first observation is that each step of (2.) requires O(s^2) time with s the number of elements in the set at the start of the iteration: this is because you match every value with every value.
A second observation is that you can never have more elements in the set than the requested value. This means that s is bounded by O(r) (we assume all coins are integers, thus the set can contain at most all integer values from 0 to r-1). Step (2.) has thus a maximum time complexity of O(r^2).
And furthermore the set evolves progressively: at each iteration, you will always construct a new value that is at least one larger than the maximum thus far. As a consequence, the algorithm will perform maximum O(r) iterations.
This implies that the algorithm has a time-complexity of O(r^3): r times O(r^2).
Why is the behavior exponential and thus at least NP-hard?
A first argument is that it comes down on how you represent input: in many cases, numbers are represented using a system with a radix greater than or equal to 2. This means that with k characters, you can represent a value that scales with O(g^k) with g the radix. Thus exponential. In other words, if you use a 32-bit number, worst case, r=O(2^32). So if you take this as input, there is an exponential part. If you would encode the target using unary notation, the algorithm is in P. But of course that's a bit like the padding-argument: given you provide enough useless input data (exponential or even super-exponential), all algorithms are in P, but you don't buy much with this.
A second argument is that if you leave the the requested value out of the input, you can only state that you start with n coins. You know that the number of iterations is fixed: you see the target value as an unknown constant. Each iteration, the total number of values in the Map<Integer,Integer> potentially squares. This thus means that the computational effort is:
n+n^2+n^4+n^6+...n^(log r)
^ ^ ^
| \-- first iteration \-- end of algorithm
\-- insertion
It is clear that this behavior is exponential in n.
The values are match with the regions,
0 -> [0,60)
100 -> [60,120)
200 -> [120,180)
...
If I have a value 160, the program should return 200, because 160 is in (120, 180).
Is there any fast way to do this ?
It would be better to use the Java to describe.
Thanks.
UPDATE:
1.Maybe there are a large of resulting labels, like this:
0, 1, 2 , ... i ..., (i++)
2.The range of regions aren't always increase by 60.
[0, 60)
[60, 100)
...
With regions of equal length you could specify the result values that correspond to the regions in an array and calculate the array index based on the input value like this:
public class Example {
private static final int[] MAPPING = {0, 100, 200};
private static final int REGION_LENGTH = 60;
public static int encode(int value) {
// TODO apply range check to avoid ArrayIndexOutOfBounds
return MAPPING[(value / REGION_LENGTH)];
}
public static void main(String[] args) {
System.out.println(encode(160)); // prints 200
}
}
Store the right ends of the regions. Then use a binary search to find the index of the lowest right end that's greater than the input value. Print/return the special value of that index.
public static int getSpecialValue(int inputValue, int specialValue[], int rightIndex[]) {
int hi = rightIndex.length - 1, low = 0; // N = number of regions
while( hi >= low ) {
int mid = (hi + low)/2;
if( rightIndex[mid] > inputValue) {
hi = mid - 1;
} else {
low = mid + 1;
}
}
return specialValue[hi];
}
Runtime Analysis: O(lg(N)), where N = number of regions.
If the regions always gets increased by 60 then you can follow the previous answers which takes O(1) runtime.
If the regions bounds are small (less than 1 million or so), you can preprocess the output in O(R) time, where R is the right index of the rightmost region and then answer each query in O(1) time.
desValue=((Inputvalue)/60)*100;
Inputvalue should be an Integer.
use a binary search tree (or just an array) .
Search on the basis of first value of the interval , i.e value <= x .
Now
if( `end` of `interval > x`)
you got the interval
else
no interval contains x.
Time complexity : O(log N).
this is a java code that recursively counts the number of payments in specific coins (ex. 1, 2, 5, 20, 50 etc.). I have tried to figure out how it works but can't seem to get it. Could somebody please be so kind and explain the math and logic behind the code and how this recursion works? I would really appreciate it.
// Returns the count of ways we can sum S[0...m-1] coins to get sum n
int count( int S[], int m, int n ){
// If n is 0 then there is 1 solution (do not include any coin)
if (n == 0)
return 1;
// If n is less than 0 then no solution exists
if (n < 0)
return 0;
// If there are no coins and n is greater than 0, then no solution exist
if (m <=0 && n >= 1)
return 0;
// count is sum of solutions (i) including S[m-1] (ii) excluding S[m-1]
return count( S, m - 1, n ) + count( S, m, n-S[m-1] );
}
The method works like this:
First statements are the stopping conditions for the current recursion (without these for all cases then you end up with an infinite loop which ultimately end in a StackOverFlow)
The final line is where the calculation occurs. Each of the statements is reducing the problem into smaller chunks by:
count( S, m - 1, n ) reduces the number of coins (m-1) which excludes the last coin in the next recursive call to
count( S, m, n-S[m-1]) uses the last coin in the array and reduces the sum that is need to reach by the value of that coin
Consider this small example:
S[] = {1,2) // We have a 1 and 2 cent coin
m = S.length // Consider all possibilities ( = 2)
n = 3 // How many ways can we make 3c
// Obviously with: 1x1c + 1x2c
// and: 3x1c
The recursion as a tree; left branch = count( S, m - 1, n ), right branch = count( S, m, n-S[m-1]):
m=2;n=3
/ \
m=1;n=3 m=2;n=1
/ \ / \
m=0;n=3 m=1;n=2 m=1;n=1 m=2;n=-1
/ \ / \
m=0;n=2 m=1;n=1 m=0;n=1 m=1;n=0
/ \
m=0;n=1 m=1;n=0
This recursion can be thought of as a Pre-order Traversal of this tree.
If you then consider the conditions of the method for where a solution is found or not. So at the leaf nodes where n = 0.
Each which comes about like this:
First solution
m=1;n=3 - Exclude the last coin (2c)
m=1;n=2 - Use this coin (1c) and reduce by 1
m=1;n=1 - Use this coin (1c) and reduce by 1
m=1;n=0 - Use this coin (1c) and reduce by 1
n = 0 - a solution (3x1c)
Second Solution
m=2;n=1 - Use this coin(2c) and reduce by 2
m=1;n=1 - Exclude the last coin (2c)
m=1;n=0 - Use this coin (1c) and reduce by 1
n = 0 - a solution (1x2c + 1x2c)
At each node a value is returned - 0 (no solution) or 1 (a solution) - to add to the total count of solutions found. Once the recursion ends this final value is returned and is the number of solutions.
Some additional notes:
This piece of code will only consider the first m coins in the array S so to consider all the possible ways the initial call to the method needs to have m == S.length
Assumes that each coin can be used multiple times
Code modification with print statements to see the recursion:
public static void main(String[] args){
int[] coins = new int[]{1,2};
System.out.println("Final Count = " + count(coins, coins.length, 3, ""));
}
public static int calls = 0;
public static int count( int S[], int m, int n , String from){
calls++;
System.out.print("Call#" + calls + ": " + from + "; m = " + m + "; n = " + n);
// If n is 0 then there is 1 solution (do not include any coin)
if (n == 0)
{
System.out.println(" - Solution Found");
return 1;
}
// If n is less than 0 then no solution exists
if (n < 0)
{
System.out.println(" - No Solution Found n < 0");
return 0;
}
// If there are no coins and n is greater than 0, then no solution exist
if (m <=0 && n >= 1)
{
System.out.println(" - No Solution Found (other Case)");
return 0;
}
System.out.println();
// count is sum of solutions (i) including S[m-1] (ii) excluding S[m-1]
return count( S, m - 1, n , from + "E" ) + count( S, m, n-S[m-1], from + "I" );
}
From the code, I'm assuming S is an array with at least m elements, with each element representing an available coin denomination, and n is the intended sum.
The comments really say it all, except that the last comment is backwards. count( S, m - 1, n ) is the number of solutions excluding the last coin in the current range. count( S, m, n-S[m-1] ) is the number of solutions using that coin.
The exclude case simply drops the last coin in the current range by reducing m by one.
The include case uses it by reducing n by that coin's value. Since the include case does not also reduce m, presumably any coin denomination can be used multiple times. It does not matter if the coin is too big - that is taken care of by returning 0 if n < 0.
If anything about the base cases is not clear from the comments, please ask a specific question.