Brute Force the Frog Jumping Game - java

The last variation of the Frog Jumping is shown at the end of the video.
In short, you have n number of lily pads in a line and one frog on
each one.
In the last variation (the one I want to brute force), the second
first and second last lily pads do not have a frog. Your goal is to
get all frogs to the same lily pad. Each frog can jump right or left
based on the number of frogs on its lily pad, but can't jump on a
empty lily pad.
(pad with 1 frog moves 1 spot, pad with n frogs moves only n spots)
Example of a solution for n=12: (there are no solutions below 12)
[1,0,1,1,1,1,1,1,1,1,0,1] - Starting formation of frogs. (counting
frogs from 0. to 11.) [1,0,1,0,2,1,1,1,1,1,0,1] - Frog 3. jumped
right [1,0,1,0,2,1,2,0,1,1,0,1] - Frog 7. jumped left
[1,0,1,0,4,1,0,0,1,1,0,1] - Frogs 6. jumped left
[5,0,1,0,0,1,0,0,1,1,0,1] - Frogs 4. jumped left
[0,0,1,0,0,6,0,0,1,1,0,1] - Frogs 0. jumped right
[0,0,1,0,0,0,0,0,1,1,0,7] - Frogs 5. jumped right
[0,0,1,0,0,0,0,0,0,2,0,7] - Frogs 8. jumped right
[0,0,1,0,0,0,0,0,0,0,0,9] - Frogs 9. jumped right
[0,0,10,0,0,0,0,0,0,0,0,0] - Frogs 11. jumped left- solved
I want to find solutions for n frogs, if the solution exists. I know by hand that 12,14,15,16,17,18,19,20 have at least one solution and that 1-11 and 13 do not have a solution.
I tried writing a piece of code that would run through all combinations to find a solution for n lily pads.
EDIT: The code works now, thanks to OleV.V., also added logging.
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Deque;
// # Brute Force # Brute Force # Brute Force # Brute Force # Brute Force # //
public class Frogger {
/**
* PUBLIC STATIC GLOBAL VARIABLES - Modify these as you wish.
*
* Time Data: Levels < 20 ~ around couple seconds
* Level = 20 ~ around a minute
* Level = 21 ~ around a quarter of an hour
* Level = 22 ~ around a sixth of a minute
* Level = 23 ~ around half an hour
* Level = 24 ~ around a minute
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
public static int Level = 12;
public static boolean LogSolution = true;
public static boolean LogAll = false;
/** * * * * * * * * * * * * * * * * * * * * * * * * * * * */
// used for logging
private static Deque<Jump> solution = new ArrayDeque<>(Level);
private static double time;
public static void main(String[] args) {
// log the time
time = System.currentTimeMillis();
// build the world & start jumping
run(Level);
}
public static void run(int n) {
// create the world
int[] world = new int[n];
for (int i = 0; i < n; i++) {
world[i] = 1;
}
world[1] = 0;
world[n-2] = 0;
// start jumping
if (Level > 11 && Level != 13) jump(world);
else System.out.println("Unsolvable");
}
//////////////////////////////////////////////////////
public static void jump(int[] world) {
for (int i = 0; i < world.length; i++) {
if (world[i] != 0) { // pad has a frog
// check if it is solved at current pad
if (world[i] == Level - 2) {
System.out.println("SOLUTION: " + Arrays.toString(world));
System.out.println(solution);
System.out.println("\n" + (System.currentTimeMillis() - time) / 1000 + " seconds");
System.exit(0);
}
// roll-back var
int temp = 0;
// attempts to make a RIGHT jump
if (world[i] + i < world.length) { // right jump is in bound
if (world[i + world[i]] != 0) { // can't jump on empty pad
temp = world[i];
// jump RIGHT
world[i + world[i]] += world[i];
world[i] = 0;
solution.push(new Jump(temp, i, i + temp)); // log the solution step 1/2
if (LogSolution) if (LogAll) System.out.println( "J: " + Arrays.toString(world)); // log the jump
// continue jumping
jump(world);
// roll-back right jump
world[i] = temp;
world[i + world[i]] -= world[i];
if (LogAll) System.out.println("R: " + Arrays.toString(world)); // log the rollback
if (LogSolution) solution.pop(); // log the solution step 2/2
}
}
// attempts to make a LEFT jump
if (i - world[i] >= 0) { // left jump is in bound
if (world[i - world[i]] != 0) { // can't jump on empty pad
temp = world[i];
// jump LEFT
world[i - world[i]] += world[i];
world[i] = 0;
if (LogSolution) solution.push(new Jump(temp, i, i - temp)); // log the solution step 1/2
if (LogAll) System.out.println("J:" + Arrays.toString(world)); // log the jump
// continue jumping
jump(world);
// roll-back left jump
world[i] = temp;
world[i - world[i]] -= world[i];
if (LogAll) System.out.println("R: " + Arrays.toString(world)); // log the rollback
if (LogSolution) solution.pop(); // log the solution step 2/2
}
}
}
}
}
}
Side note: This problem was mathematically solved for all solvable n (all n > 11, other than 13, have a solution, reachable by a generalized method). This piece of code is just me attempting to do some recursion in java.

Glad you got it to work. I don’t think you need my code now, but I will give at the bottom of this answer in case.
First, how does one log a solution? I guess you’re thinking that knowing that the end result was [0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0] is not that interesting; we’d like to know how it was obtained. I will present two ways.
The easier way is store each step as it is tried and then delete it when backtracking. Then when you get to the solution, you have also stored the steps that lead to it. Use a stack or similar:
private static Deque<Jump> solution = new ArrayDeque<>(Level);
(java.util.ArrayDeque is the recommended class for both stacks and queues; for a stack ArrayList is another option.) Now in your code, where it says log the jump do
solution.push(new Jump(temp, i, i + temp));
At log the rollback do
solution.pop();
Use a simple auxiliary class Jump that could for instance look like this:
public class Jump {
int count;
int from;
int to;
public Jump(int count, int from, int to) {
this.count = count;
this.from = from;
this.to = to;
}
#Override
public String toString() {
return "" + count + " frog/s jump from " + from + " to " + to;
}
}
When I tried it in my solution, one search took 20 % longer. I’d say it’s acceptable. If you are very concerned about performance, only log on your way out from having found the solution. This will require you to return a boolean to indicate success rather than using System.exit() for stopping the search. Now your recursive call becomes:
if (jump(world)) {
solution.push(new Jump(temp, i, i + temp));
return true;
}
You get the elements in your solution stack in the opposite order than before, I expect you to figure that out. Also instead of System.exit(0); do return true;. At the bottom of the method, return false. I have not measured performance impact, but I expect it to be minute compared to logging nothing. Now you can get output like:
1 frog/s jump from 3 to 4
1 frog/s jump from 7 to 6
2 frog/s jump from 6 to 4
4 frog/s jump from 4 to 0
5 frog/s jump from 0 to 5
6 frog/s jump from 5 to 11
1 frog/s jump from 8 to 9
2 frog/s jump from 9 to 11
9 frog/s jump from 11 to 2
Finally here’s how I did, just for the sake of completeness. I haven’t spotted any interesting differences from your code.
public static void jump(int[] world) {
for (int fromIndex = 0; fromIndex < world.length; fromIndex++) { // index of pad to jump from
// any frog/s here?
int frogsJumping = world[fromIndex];
if (frogsJumping > 0) {
// try to jump left; frogsJumping frogs jump frogsJumping places
int targetIndex = fromIndex - frogsJumping;
if (targetIndex >= 0 && world[targetIndex] > 0) { // must not jump to empty pad
performJump(fromIndex, targetIndex, world, frogsJumping);
}
// try a right jump
targetIndex = fromIndex + frogsJumping;
if (targetIndex < world.length && world[targetIndex] > 0) {
performJump(fromIndex, targetIndex, world, frogsJumping);
}
}
}
}
private static void performJump(int fromIndex, int toIndex, int[] world, int frogsJumping) {
solution.push(new Jump(frogsJumping, fromIndex, toIndex));
world[fromIndex] = 0;
world[toIndex] += frogsJumping;
if (world[toIndex] == noOfFrogs) {
System.out.println("Solved: " + Arrays.toString(world));
System.exit(0);
}
jump(world);
// backtrack
world[toIndex] -= frogsJumping;
world[fromIndex] = frogsJumping;
solution.pop();
}

Related

Indenting an output with recursion

I am trying to use recursion to print out two lines of text each and with an indent every time there's recursion (example in javadoc below). I know using (numCalls + 1) will allow it to increase the indent each time but I'm not sure how to actually put it together so does what it is asking for.
public class Practice1 {
/**
* Prints 2 lines of text for each recursive call, indicating call number
* (a value >= 1, and <= value of numCalls, as illustrated below. Each
* level of recursion should be indicated by indenting the input line by
* r spaces. For example, if numCalls is 3, the method should print:
* call 1
* call 2
* call 3
* back 3
* back 2
* back 1
* #param r the level of method calls
* #param numCalls the number of intended levels
*/
public static void stairSteps(int r, int numCalls) {
if (r == 0) {
System.out.println("Done");
} else {
System.out.println("call " + r);
System.out.println("back " + r);
r--;
numCalls++;
stairSteps(r, numCalls);
}
}
public static void stairSteps(int totalLevels, int indent) {
stairSteps(1, totalLevels, indent);
System.out.println("Done");
}
private static void stairSteps(int level, int totalLevels, int indent) {
if (level <= totalLevels) {
int offs = indent * (level - 1);
System.out.println(" ".repeat(offs) + "call " + level);
stairSteps(level + 1, totalLevels, indent);
System.out.println(" ".repeat(offs) + "back " + level);
}
}
Output:
stairSteps(3, 2);
call 1
call 2
call 3
back 3
back 2
back 1
Done

Value of Variable Keeps Resetting Every Time The Loop Runs (Java)

I'm currently working on a homework assignment for my Java OOP class. The assignment is that a mouse is trapped midway in a 30ft long pipe. The mouse needs to move randomly with a 50% chance of going left or right. The mouse can only move a random distance between a min of 3ft and a max distance of 8ft for each move made. The program should keep track of the distance that the mouse is from the left and right ends of the pipe after each move and once it has reached one end of the pipe, the loop should end and it should print out that the mouse has reached whatever end it reached. I'm using the variable fromendL to keep track of the mouse's distance from the left end and fromendR for the other end. The problem i'm encountering is that every time the loop runs it's resetting the values of each variable so and it's never giving me the distance over the course of multiple loops. Everything I believe I have working fine, although it might not be the most efficient way (i'm still fairly new to programming).
I'll attach the code below:
class Main {
public static void main(String[] args)
{
int fromendL;
int fromendR;
int tMin = 3;
int tMax = 8;
int direction;
int travel;
int travelR = 15;
int travelL = 15;
boolean escaped = false;
while (!escaped)
{
direction = (int)(Math.random()* 2 + 1);
travel = (int)(Math.random() * (tMax - tMin) + 3);
if (direction == 1)
{
fromendL = 15 - travel;
fromendR = 30 - 15 + travel;
travelL = travelL - travel;
System.out.println("The mouse has moved: " + travel + " ft to the left" );
System.out.println("The mouse is " + fromendL +" ft from The Left End");
System.out.println("The mouse is " + fromendR +" ft from The Right End\n");
if(travelL == 0 || travelL < 0)
{
escaped = true;
System.out.println("The Mouse has escaped the Left end of the Pipe!");
}
else if (travelR == 30 || travelR > 30)
{
escaped = true;
System.out.println("The Mouse has escaped the Right end of the Pipe!");
}
}
else if (direction == 2)
{
fromendL = 15 + travel;
fromendR = 30 - 15 - travel;
travelR = travelR + travel;
System.out.println("The mouse has moved: " + travel + " ft to the right");
System.out.println("The mouse is " + fromendL +" ft from The Left End");
System.out.println("The mouse is " + fromendR +" ft from The Right End\n");
if(travelR == 30 || travelR > 30)
{
escaped = true;
System.out.println("The Mouse has escaped the Right end of the Pipe!");
}
else if (travelL == 0 || travelL < 0)
{
escaped = true;
System.out.println("The Mouse has escaped the Left end of the Pipe!");
}
}
}
}
}
The following piece of code should solve the original problem. Please analyze it to find some of the improvements compared to your code.
Instead of using two variables to keep the position (fromendLand fromendR), I use a single one (pos). If I know the length of the pipe (which doesn't change) and the position starting from the left side of the pipe, the distance to the right end can be calculated.
Constructs like if (a == 0 || a < 0) are better written like if (a <= 0) in my opinion.
I use the direction as +1 or -1. Just like in mathematics, the negative direction is like going to the left, the positive direction is like going to the right.
The reason that your variables are reset every time lies in
fromendL = 15 - travel;
fromendR = 30 - 15 + travel;
travelL = travelL - travel;
You start from 15 resp 30 over and over again instead of using the last position of the mouse.
My code looks like
(I use tabs for indentation; my eyes are too old to see the subtle 2-character indentations ;) :
class Main {
public static void main(String[] args)
{
static final int tMin = 3;
static final int tMax = 8;
static final int pLength = 30;
int direction;
int travel;
int pos = pLength / 2;
boolean escaped = false;
while (!escaped) {
direction = (int)(Math.random() * 2);
if (direction == 0) direction = -1;
travel = ((int)(Math.random() * (tMax - tMin)) + tMin) * direction;
pos += travel;
System.out.println("The mouse has moved: " + Math.abs(travel) + " ft to the " + (travel > 0 ? "right" : "left"));
System.out.println("The mouse is " + pos +" ft from The Left End");
System.out.println("The mouse is " + (pLength - pos) + " ft from The Right End\n");
if (pos >= pLength || pos <= 0) {
System.out.println("The Mouse has escaped the " + (pos <= 0 ? "Left" : "Right") + " end of the Pipe!");
escaped = true;
}
}
}
}
The ? is a ternary operator. It tests the condition and if true, the expression evaluates to the value directly following the question mark. If false the expression evaluates to the value following the colon. Use it with care, many people don't like it and think it's confusing. And for that reason I've put it between parenthesis so it's clear where the construct starts and ends.
Since pos is either less or equal zero (escaped to the left) or greater or equal 30 (escaped to the right), I only have to test which of both it is. If I know the mouse didn't escape to the left it must have escaped to the right. (The condition of the if already guarantees that the mouse has escaped).

Minimum number of powers of 2 to get an Integer?

I was asked below question in an interview:
Every number can be described via the addition and subtraction of powers of 2. For example, 29 = 2^0 + 2^2 + 2^3 + 2^4.
Given an int n, return minimum number of additions
and subtractions of 2^i to get n.
Example 1:
Input: 15
Output: 2
Explanation: 2^4 - 2^0 = 16 - 1 = 15
Example 2:
Input: 8
Output: 1
Example 3:
Input: 0
Output: 0
Below is what I got but is there any way to improve this or is there any better way to solve above problem?
public static int minPowerTwo(int n) {
if (n == 0) {
return 0;
}
if (Integer.bitCount(n) == 1) {
return 1;
}
String binary = Integer.toBinaryString(n);
StringBuilder sb = new StringBuilder();
sb.append(binary.charAt(0));
for (int i = 0; i < binary.length() - 1; i++) {
sb.append('0');
}
int min = Integer.parseInt(sb.toString(), 2);
sb.append('0');
int max = Integer.parseInt(sb.toString(), 2);
return 1 + Math.min(minPowerTwo(n - min), minPowerTwo(max - n));
}
Well... we can deduce that each power of two should be used only once, because otherwise you can get the same result a shorter way, since 2x + 2x = 2x+1, -2x - 2x = -2x+1, and 2x - 2x = 0.
Considering the powers used in order, each one has to change the corresponding bit from an incorrect value to the correct value, because there will be no further opportunities to fix that bit, since each power is used only once.
When you need to add or subtract, the difference is what happens to the higher bits:
000000 000000 111100 111100
+ 100 - 100 + 100 - 100
------ ------ ------ ------
000100 111100 000000 111000
One way, all the higher bits are flipped. The other way they are not.
Since each decision can independently determine the state of all the higher bits, the consequences of choosing between + or - are only relevant in determining the next power of 2.
When you have to choose + or -, one choice will correct 1 bit, but the other choice will correct 2 bits or more, meaning that the next bit that requires correction will be higher.
So, this problem has a very straightforward solution with no dynamic programming or searching or anything like that:
Find the smallest power of 2 that needs correction.
Either add it or subtract it. Pick the option that corrects 2 bits.
Repeat until all the bits are correct
in java, that would look like this. Instead of finding the operations required to make the value, I'll find the operations required to change the value to zero, which is the same thing with opposite signs:
int minPowersToFix(int val) {
int result = 0;
while(val!=0) {
++result;
int firstbit = val&-val; //smallest bit that needs fixed
int pluscase = val+firstbit;
if ((pluscase & (firstbit<<1)) == 0) {
val+=firstbit;
} else {
val-=firstbit;
}
}
return result;
}
And, here is a test case to check whether a solution is correct, written in Java.
(It was written for my solution, which is proven not correct in some case, so I removed that answer, but the test case is still relevant.)
Matt Timmermans's answer passes all the test cases, including negative numbers.
And, Integer.bitCount(val ^ (3 * val)) passes most of them, except when input is Integer.MAX_VALUE.
Code
MinCountOf2PowerTest.java
import org.testng.Assert;
import org.testng.annotations.Test;
public class MinCountOf2PowerTest {
#Test
public void testPositive() {
// no flip,
Assert.assertEquals(MinCountOf2Power.minCount(Integer.parseInt("01010001", 2)), 3);
// flip,
Assert.assertEquals(MinCountOf2Power.minCount(Integer.parseInt("011", 2)), 2);
Assert.assertEquals(MinCountOf2Power.minCount(Integer.parseInt("0111", 2)), 2);
Assert.assertEquals(MinCountOf2Power.minCount(Integer.parseInt("01111", 2)), 2);
Assert.assertEquals(MinCountOf2Power.minCount(Integer.MAX_VALUE), 2);
Assert.assertEquals(MinCountOf2Power.minCount(Integer.parseInt("01101", 2)), 3);
Assert.assertEquals(MinCountOf2Power.minCount(Integer.parseInt("011011", 2)), 3);
// flip, there are multiple flippable location,
Assert.assertEquals(MinCountOf2Power.minCount(Integer.parseInt("0100000111", 2)), 3);
Assert.assertEquals(MinCountOf2Power.minCount(Integer.parseInt("010010000000111", 2)), 4);
Assert.assertEquals(MinCountOf2Power.minCount(Integer.parseInt("0100100000001111111", 2)), 4);
Assert.assertEquals(MinCountOf2Power.minCount(Integer.parseInt("010011000000001111111", 2)), 5);
}
#Test
public void testZero() {
Assert.assertEquals(MinCountOf2Power.minCount(0), 0);
}
#Test
public void testNegative() {
Assert.assertEquals(MinCountOf2Power.minCount(-1), 1);
Assert.assertEquals(MinCountOf2Power.minCount(-9), 2);
Assert.assertEquals(MinCountOf2Power.minCount(-100), 3);
}
// a positive number has the same result as its negative number,
#Test
public void testPositiveVsNegative() {
for (int i = 1; i <= 1000; i++) {
Assert.assertEquals(MinCountOf2Power.minCount(i), MinCountOf2Power.minCount(-i));
}
Assert.assertEquals(MinCountOf2Power.minCount(Integer.MAX_VALUE), MinCountOf2Power.minCount(-Integer.MAX_VALUE));
}
// corner case - ending 0,
#Test
public void testCornerEnding0() {
Assert.assertEquals(MinCountOf2Power.minCount(Integer.parseInt("01110", 2)), 2);
Assert.assertEquals(MinCountOf2Power.minCount(Integer.parseInt("011110", 2)), 2);
Assert.assertEquals(MinCountOf2Power.minCount(Integer.parseInt("011100", 2)), 2);
Assert.assertEquals(MinCountOf2Power.minCount(Integer.parseInt("0111000", 2)), 2);
Assert.assertEquals(MinCountOf2Power.minCount(Integer.parseInt("01110000", 2)), 2);
}
// input from OP's question, refer: https://stackoverflow.com/questions/57797157
#Test
public void testOpInput() {
Assert.assertEquals(MinCountOf2Power.minCount(15), 2);
Assert.assertEquals(MinCountOf2Power.minCount(8), 1);
Assert.assertEquals(MinCountOf2Power.minCount(0), 0);
}
}
Tips:
It's written in Java, and use TestNG.
But you can use JUnit instead simply by replacing the import statement, I guess.
Or translate to other languages by coping the input / output value pairs with specific syntax.
I also found that a positive integer always has the same result as its negative number.
And there is a test case included to proved that.
I wrote this algorithm to solve the problem.
Given N a positive integer:
Find the highest power of 2 A and the lowest power of 2 B, such that A ≤ N ≤ B and A≠B. In other words find in what interval of
consecutive powers of 2 N belongs;
Find if N is closer to A or B, for example by comparing N with the mid value between A and B (It is their average, and since B=2×A the average is 3×A/2 or 1.5×A)
If N is closer to the lower bound (A) than N = A + δ: Append "subtract B" to the explanation message;
If N is closer to the higher bound (B) than N = B - δ: Append "add A" to the explanation message;
Replace N with δ and repeat
The number of iterations minus 1 is the solution you are looking for.
To solve step 1 I wrote this support method that returns the closest power of 2 that is smaller than input, that is A (and we can get B because it is just the double of A)
public int getClosestLowerboundPowerof2 (int n)
{
int i = 1;
while (i<=n/2){
i*=2;
}
return i;
}
The rest is done here:
int operations;
String explanation = "";
if (input>0){
operations = -1;
int n = input, a;
while (n >= 1) {
operations++;
a = getClosestLowerboundPowerof2(n);
if (n > a*1.5) {
explanation += " - "+ a * 2;
n = a * 2 - n;
} else {
explanation += " + " + a;
n -= a;
}
}
System.out.println(input + " = " + explanation.substring(3,explanation.length()) + ", that " + ((operations==1)?"is":"are") + " "+ operations + " operation" + ((operations==1)?"":"s"));
}
else{
System.out.println("Input must be positive");
}
As an example with input = 403 it would print:
403 = 512 - 128 + 16 + 2 + 1, that are 4 operations
Hope I helped!
NOTE: I first misinterpreted the question so I put effort in writing a detailed answer to the wrong problem...
I'm keeping here the original answer because it may be interesting for somebody.
The problem is actually a mathematical argument: how to convert a
number from base 10 to base 2, and they just asked you to implement
an algorithm for that.
Here some theory about this concept and here a method for
reference.
Programmatically I'm interpreting the problem as "Given an integer
print a string of its representation in base 2". For instance given
100 print 2^6 + 2^5 + 2^2. As the linked wiki on radixes explains,
that there is no need for subtractions, so there will only be
additions.
The shortest way to do is to start from n, halve it at each iteration
(i), and write 2^i only if this number (m) is odd. This is tested
with modulo operator (% in java). So the method will be just
this:
public String from10to2(int n){
String str = "";
for (int m = n, i=0; m>=1; m/=2, i++){
str = ((m%2==1)?"+ 2^"+i+" ":"")+str; //"adds '+ 2^i' on top of the string when m is odd, keep str the same otherwise
}
return str.substring(2,str.length()); //debug to remove " + " at the start of the string
}
The content of the for may look inintuitive because I put effort to
make the code as short as possible.
With little effort my method can be generalized to convert a number in
base 10 to any base:
public String baseConverter(int targetBase, int decimalNumber){
String str = "";
for (int m = decimalNumber, i=0; m>=1; m/=targetBase, i++){
str = ((m%targetBase==1)?"+ "+targetBase+"^"+i+" ":"")+str; //"adds '+ x^i' on top of the string when m is odd, keep str the same
otherwise
}
return str.substring(2,str.length()); //debug to remove " + " at the start of the string
}
PS: I didn't use StringBuilder because it's not conceived to append a string on the start. The use of the String concatenation as I
did is argument of debate (someone approve it, other don't).
I guess
For example, 29 = 2^0 + 2^2 + 2^3 + 2^4
is not a correct example in the context of this question. As far as I understand, I should be able to do like
29 = 2^5 - 2^2 + 2^0
Alright, basically this is a math problem. So if math isn't your best suit like me then i would advise you to consider logarithm in the first place whenever you see exponentials in a question. Sometimes it is very useful like in this case since it reduces this problem to a sort of coin change problem with dynamical denominators and also subtraction is allowed.
First I need to find the biggest n that's close to the target.
Lets find the exact n value in 2^n = 29 which is basically log
(2^n) = log 29, which is n log 2 = log 29 so n = log 29 / log
2. Which happens to be 4.857980995127573 and now i know that i
will start with by rounding it to 5.
2^5 is an overshoot. Now i need to reach 32-29 = 3 and also since 32 > 29 the result, 2^2 will be subtracted.
Now we have 2^5 - 2^2 which is 28 and less than 29. Now we need to add the next result and our target is 1.
Ok here is a simple recursive code in JS. I haven't fully tested but seemingly applies the logic just fine.
function pot(t, pr = 0){ // target and previous result
var d = Math.abs(t - pr), // difference
n = Math.round(Math.log(d)/Math.log(2)), // the n figure
cr = t > pr ? pr + 2**n // current result
: pr - 2**n;
return t > cr ? `2^${n} + ` + pot(t, cr) // compose the string result
: t < cr ? `2^${n} - ` + pot(t, cr)
: `2^${n}`;
}
console.log(pot(29));
console.log(pot(1453));
console.log(pot(8565368));
This seems pretty trivial to solve for the cases presented in the examples, like:
0111...1
You can replace any of this pattern with just two powers; i.e.: 7 = 8 - 1 or 15 = 16 - 1 and so on.
You can also deduce that if there are less then 3 consecutive ones, you don't gain much, for example:
0110 (4 + 2)
0110 (8 - 2)
But at the same time, you don't lose anything by doing that operation; in contrast for some cases this is even beneficial:
0110110 - // 54, this has 4 powers
we can take the "last" 0110 and replace it with 1000 - 0010 (8-2) or:
0111000 - 000010 (56 - 2)
but now we can replace 0111 with just two powers : 1000 - 0001.
As such a simple "replace" algorithm can be made:
static int count(int x) {
String s = new StringBuffer(Integer.toBinaryString(x)).reverse().toString() + "0";
Pattern p = Pattern.compile("1+10");
Matcher m = p.matcher(s);
int count = 0;
while (m.find()) {
++count;
s = m.replaceFirst("1");
m = p.matcher(s);
}
return Integer.bitCount(Integer.parseInt(s, 2)) + count;
}

Head First Java Puzzle4 (Chapter 4, pg 91) Explanation

Below is the solution to, and output of,the subject puzzle, and I'm having trouble understanding how it works.
public class Puzzle4 {
public static void main(String[] args) {
Puzzle4b[] obs = new Puzzle4b[6];
int y = 1;
int x = 0;
int result = 0;
while (x<6) {
obs[x] = new Puzzle4b();
obs[x].ivar = y;
y = y*10;
x = x+1;
}
x = 6;
while (x>0) {
x = x-1;
result = result + obs[x].doStuff(x);
}
System.out.println("result " + result);
}
}
class Puzzle4b {
int ivar;
public int doStuff(int factor) {
if (ivar>100) {
return ivar*factor;
} else {
return ivar*(5-factor);
}
}
}
Output:
result 543345
From my understanding, the first While loop will run through 6 times (from x==0 to x==5). The y variable, and in turn 'ivar', will have a value of 1,000,000 (I think this is where I'm going wrong, but I'll continue in hopes of being corrected).
The second While loop is a bit confusing to me. It'll run through 6 times, with the second line of the loop passing the 'x' values to the doStuff method for it to return a value. The numbers I'm coming up with for the result don't match the actual result.
Any help here would be appreciated. Please let me know if I'm thinking about it the wrong way. If someone wants to reformat my code to align more closely with industry standards, it would be great to learn good habits from the start!
this piece of code `
obs[x].ivar = y;
y = y*10;
will cause first ivar = y = 1 then multiple y
so it'll be {1 - 10 - 100 - 1,000 - 10,000 - 100,000}
the second loop x will be { 5 , 4 , 3 , 2 , 1 , 0 }
so it'll start like this result = result+obs[5].doStuff[5];
i guess all the misunderstanding that x in element 1 will equal y in element 5
and x in element 2 will equal y in element 4 and so on " because x is incrementing and y is decrementing
so it will go like this
(ivar > 100 )
ivar * (5-factor)
x[3] * y[2] = 1000 * (5-2) = 3000
x[4] * y[1] = 10000 * (5-1) = 40000
x[5] * y [0] = 100000 * (5-0) = 500000
then (ivar < 100)
ivar * factor
x[1] * y [5] = 1 * 5 = 5
x[2] * y [4] = 10 * 4 = 40
x[3] * y [3] = 100 * 3= 300
then if you add all the numbers together you'll have your output
In the second while loop,
while(x>0) will give 543340
Instead While (x>=0) should give the required output
If I'm not wrong
For me an easy way to break this down is stepping through and seeing exactly where the values are coming from. I've done this below and commented it into the code.
public class Puzzle4 {
public static void main(String[] args) {
Puzzle4b[] obs = new Puzzle4b[6];
int y = 1;
int x = 0;
int result = 0;
// let's break out of the loops
obs[0] = new Puzzle4b();
obs[0].ivar = 1;
obs[1] = new Puzzle4b();
obs[1].ivar = 10;
obs[2] = new Puzzle4b();
obs[2].ivar = 100;
obs[3] = new Puzzle4b();
obs[3].ivar = 1000;
obs[4] = new Puzzle4b();
obs[4].ivar = 10000;
obs[5] = new Puzzle4b();
obs[5].ivar = 100000;
// ivar's = 1, 10, 100, 1000, 10000, 100000
result = result + obs[5].doStuff(5); // 0 + (5 * 100000)
result = result + obs[4].doStuff(4); // 500000 + (4 * 10000)
result = result + obs[3].doStuff(3); // 540000 + (3 * 1000)
result = result + obs[2].doStuff(2); // 543000 + ((5 - 2) * 100)
result = result + obs[1].doStuff(1); // 543300 + ((5-1) * 10)
result = result + obs[0].doStuff(0); // 543340 + ((5-0) * 1)
// result = 543345
}
System.out.println("result " + result);
}
}
class Puzzle4b {
int ivar;
public int doStuff(int factor) {
if (ivar>100) {
return ivar*factor;
} else {
return ivar*(5-factor);
}
}
}
Hope this has helped you understand the example.
The key is to understand what each loop does.
1st one is nothing but just assigning values to ivar variable of each array object. You do it with "x" value as index.
2nd one you still get indices from what "x" variable currently is. And then you need to remember what ivar value is for exactly this index. With that in mind you will be able to understand what condition of doStuff method is true and what expression you should use respectively.
Also, you have to remember of what value "result" variable get on each loop iteration. Like 1st iteration result = 500 000. then 2nd one will be result = 500 000 + doStuff.
And last but not least, not without reason the book calls you to do exercises on paper. It leads to a lot of writing but also to understanding what code does on each and every line.

Starting from 1 how far can I count, when I can use any digit a maximum of N times

My problem is as follows; for number N, I need to find out what is the largest value I can count to, when each digit can be used N times.
For example if N = 5, the largest value is 12, since at that point the digit 1 has been used 5 times.
My original approach was to simply iterate through all numbers and keep a tally of how many times each digit has been used so far. This is obviously very inefficient when N is large, so am looking for advice on what would be a smarter (and more efficient) way to achieve this.
public class Counter {
private static Hashtable<Integer, Integer> numbers;
public static void main(String[] args){
Counter c = new Counter();
c.run(9);
}
public Counter() {
numbers = new Hashtable<Integer, Integer>();
numbers.put(0, 0);
numbers.put(1, 0);
numbers.put(2, 0);
numbers.put(3, 0);
numbers.put(4, 0);
numbers.put(5, 0);
numbers.put(6, 0);
numbers.put(7, 0);
numbers.put(8, 0);
numbers.put(9, 0);
}
public static void run(int maxRepeat) {
int keeper = 0;
for(int maxFound = 0; maxFound <= maxRepeat; maxFound++) {
keeper++;
for (int i = 0; i < Integer.toString(keeper).length(); i++) {
int a = Integer.toString(keeper).charAt(i);
//here update the tally for appropriate digit and check if max repeats is reached
}
}
System.out.println(keeper);
}
}
For starters, rather than backing your Counter with a Hashtable, use an int[] instead. When you know exactly how many elements your map has to have, and especially when the keys are numbers, an array is perfect.
That being said, I think the most effective speedup is likely to come from better math, not better algorithms. With some experimentation (or it may be obvious), you'll notice that 1 is always the first digit to be used a given number of times. So given N, if you can find which number is the first to use the digit 1 N+1 times, you know your answer is the number right before that. This would let you solve the problem without actually having to count that high.
Now, let's look at how many 1's are used counting up to various numbers. Throughout this post I will use n to designate a number when we are trying to figure out how many 1's are used to count up to a number, whereas capital N designates how many 1's are used to count up to something.
One digit numbers
Starting with the single-digit numbers:
1: 1
2: 1
...
9: 1
Clearly the number of 1's required to count up to a one-digit number is... 1. Well, actually we forgot one:
0: 0
That will be important later. So we should say this: the number of 1's required to count up to a one-digit number X is X > 0 ? 1 : 0. Let's define a mathematical function f(n) that will represent "number of 1's required to count up to n". Then
f(X) = X > 0 ? 1 : 0
Two-digit numbers
For two-digit numbers, there are two types. For numbers of the form 1X,
10: 2
11: 4
12: 5
...
19: 12
You can think of it like this: counting up to 1X requires a number of 1's equal to
f(9) (from counting up to 9) plus
1 (from 10) plus
X (from the first digits of 11-1X inclusive, if X > 0) plus
however many 1's were required to count up to X
Or mathematically,
f(1X) = f(9) + 1 + X + f(X)
Then there are the two-digit numbers higher than 19:
21: 13
31: 14
...
91: 20
The number of 1's required to count to a two-digit number YX with Y > 1 is
f(19) (from counting up to 19) plus
f(9) * (Y - 2) (from the 1's in numbers 20 through (Y-1)9 inclusive - like if Y = 5, I mean the 1's in 20-49, which come from 21, 31, 41) plus
however many 1's were required to count up to X
Or mathematically, for Y > 1,
f(YX) = f(19) + f(9) * (Y - 2) + f(X)
= f(9) + 1 + 9 + f(9) + f(9) * (Y - 2) + f(X)
= 10 + f(9) * Y + f(X)
Three-digit numbers
Once you get into three-digit numbers, you can kind of extend the pattern. For any three-digit number of the form 1YX (and now Y can be anything), the total count of 1's from counting up to that number will be
f(99) (from counting up to 99) plus
1 (from 100) plus
10 * Y + X (from the first digits of 101-1YX inclusive) plus
however many 1's were required to count up to YX in two-digit numbers
so
f(1YX) = f(99) + 1 + YX + f(YX)
Note the parallel to f(1X). Continuing the logic to more digits, the pattern, for numbers which start with 1, is
f(1[m-digits]) = f(10^m - 1) + 1 + [m-digits] + f([m-digits])
with [m-digits] representing a sequence of digits of length m.
Now, for three-digit numbers ZYX that don't start with 1, i.e. Z > 1, the number of 1's required to count up to them is
f(199) (from counting up to 199) plus
f(99) * (Z - 2) (from the 1's in 200-(Z-1)99 inclusive) plus
however many 1's were required to count up to YX
so
f(ZYX) = f(199) + f(99) * (Z - 2) + f(YX)
= f(99) + 1 + 99 + f(99) + f(99) * (Z - 2) + f(YX)
= 100 + f(99) * Z + f(YX)
And the pattern for numbers that don't start with 1 now seems to be clear:
f(Z[m-digits]) = 10^m + f(10^m - 1) * Z + f([m-digits])
General case
We can combine the last result with the formula for numbers that do start with 1. You should be able to verify that the following formula is equivalent to the appropriate case given above for all digits Z 1-9, and that it does the right thing when Z == 0:
f(Z[m-digits]) = f(10^m - 1) * Z + f([m-digits])
+ (Z > 1) ? 10^m : Z * ([m-digits] + 1)
And for numbers of the form 10^m - 1, like 99, 999, etc. you can directly evaluate the function:
f(10^m - 1) = m * 10^(m-1)
because the digit 1 is going to be used 10^(m-1) times in each of the m digits - for example, when counting up to 999, there will be 100 1's used in the hundreds' place, 100 1's used in the tens' place, and 100 1's used in the ones' place. So this becomes
f(Z[m-digits]) = Z * m * 10^(m-1) + f([m-digits])
+ (Z > 1) ? 10^m : Z * ([m-digits] + 1)
You can tinker with the exact expression, but I think this is pretty close to as good as it gets, for this particular approach anyway. What you have here is a recursion relation that allows you to evaluate f(n), the number of 1's required to count up to n, by stripping off a leading digit at each step. Its time complexity is logarithmic in n.
Implementation
Implementing this function is straightforward given the last formula above. You can technically get away with one base case in the recursion: the empty string, i.e. define f("") to be 0. But it will save you a few calls to also handle single digits as well as numbers of the form 10^m - 1. Here's how I'd do it, omitting a bit of argument validation:
private static Pattern nines = Pattern.compile("9+");
/** Return 10^m for m=0,1,...,18 */
private long pow10(int m) {
// implement with either pow(10, m) or a switch statement
}
public long f(String n) {
int Z = Integer.parseInt(n.substring(0, 1));
int nlen = n.length();
if (nlen == 1) {
return Z > 0 ? 1 : 0;
}
if (nines.matcher(n).matches()) {
return nlen * pow10(nlen - 1);
}
String m_digits = n.substring(1);
int m = nlen - 1;
return Z * m * pow10(m - 1) + f_impl(m_digits)
+ (Z > 1 ? pow10(m) : Z * (Long.parseLong(m_digits) + 1));
}
Inverting
This algorithm solves the inverse of the the question you're asking: that is, it figures out how many times a digit is used counting up to n, whereas you want to know which n you can reach with a given number N of digits (i.e. 1's). So, as I mentioned back in the beginning, you're looking for the first n for which f(n+1) > N.
The most straightforward way to do this is to just start counting up from n = 0 and see when you exceed N.
public long howHigh(long N) {
long n = 0;
while (f(n+1) <= N) { n++; }
return n;
}
But of course that's no better (actually probably worse) than accumulating counts in an array. The whole point of having f is that you don't have to test every number; you can jump up by large intervals until you find an n such that f(n+1) > N, and then narrow down your search using the jumps. A reasonably simple method I'd recommend is exponential search to put an upper bound on the result, followed by a binary search to narrow it down:
public long howHigh(long N) {
long upper = 1;
while (f(upper + 1) <= N) {
upper *= 2;
}
long lower = upper / 2, mid = -1;
while (lower < upper) {
mid = (lower + upper) / 2;
if (f(mid + 1) > N) {
upper = mid;
}
else {
lower = mid + 1;
}
}
return lower;
}
Since the implementation of f from above is O(log(n)) and exponential+binary search is also O(log(n)), the final algorithm should be something like O(log^2(n)), and I think the relation between N and n is linear enough that you could consider it O(log^2(N)) too. If you search in log space and judiciously cache computed values of the function, it might be possible to bring it down to roughly O(log(N)). A variant that might provide a significant speedup is sticking in a round of interpolation search after determining the upper bound, but that's tricky to code properly. Fully optimizing the search algorithm is probably a matter for another question though.
This should be more efficient. Use integer array of size 10 to keep the count of digits.
public static int getMaxNumber(int N) {
int[] counts = new int[10];
int number = 0;
boolean limitReached = false;
while (!limitReached) {
number++;
char[] digits = Integer.toString(number).toCharArray();
for (char digit : digits) {
int count = counts[digit - '0'];
count++;
counts[digit - '0'] = count;
if (count >= N) {
limitReached = true;
}
}
}
return number;
}
UPDATE 1: As #Modus Tollens mentioned initial code has a bug. When N = 3 it returns 11, but there are four 1s between 1 and 11. The fix is to check if limit is breached count[i] > N on given number, previous number should be return. But if for some i count[i] == N for other j count[j] <= N, the actual number should be returned.
Please see corresponding code below:
public static int getMaxNumber(int N) {
int[] counts = new int[10];
int number = 0;
while (true) {
number++;
char[] digits = Integer.toString(number).toCharArray();
boolean limitReached = false;
for (char digit : digits) {
int count = counts[digit - '0'];
count++;
counts[digit - '0'] = count;
if (count == N) {
//we should break loop if some count[i] equals to N
limitReached = true;
} else if (count > N) {
//previous number should be returned immediately
//, if current number gives more unique digits than N
return number - 1;
}
}
if (limitReached) {
return number;
}
}
}
UPDATE 2: As #David Z and #Modus Tollens mentioned, in case if N=13, 30 should be returned, ie, algo stops when N is breached but not reached. If this is initial requirement, the code will be even simpler:
public static int getMaxNumber(int N) {
int[] counts = new int[10];
int number = 0;
while (true) {
number++;
char[] digits = Integer.toString(number).toCharArray();
for (char digit : digits) {
int count = counts[digit - '0'];
count++;
counts[digit - '0'] = count;
if (count > N) {
return number - 1;
}
}
}
}

Categories

Resources