Optimizing digit <= 2 algorithm (similar to Project Euler 303) - java

I'm creating a program to solve Project Euler Problem 303
My method to find f(n) is barely short of brute force:
static BigInteger findFn(int n){
Long Fn = new Long(n);
String test = Fn.toString();
Long multiplier = new Long("1");
long counter = 0;
boolean done = false;
BigInteger fn = new BigInteger("0");
while(!done){
counter = 0;
BigInteger tempOne = new BigInteger(multiplier.toString());
BigInteger tempTwo = new BigInteger(Fn.toString());
BigInteger product = tempOne.multiply(tempTwo);
test = product.toString();
for(int i = 0; i < test.toString().length(); i++){
if(Character.getNumericValue(test.toString().charAt(i)) <= 2){
counter++;
}else{
//Is it better to set done = true here as opposed to breaking?
break; //breaks if it encounters any number in the multiple >2.
}
}
if(counter == test.length()){
fn = product;
done = true;
break;
}
multiplier++;
}
return fn;
}
It works well on most numbers, but there are a few (usually those that end in 9) that it just gets stuck on.
I think that the BigIntegers slow it down, so firstly, is there anywhere I've used a BigInteger where it isn't necessary?
Secondly, there has to be either an alternate method or some sort of other trick to cut down on the number of loops that I haven't thought of.
Any thoughts to give me a push in the right direction?
Thanks!!

I'm thinking you could cut out 67% of your trials just by looking at the digit in the one's place in the tested number because if that doesn't go to 0, 1, or 2 then it doesn't matter what the rest go to.
Consider that if the number ends in a 1, then the number it is multiplied by must end with a 0, 1, or 2 in order for the last digit of the result to be <= 2. So you test 1 then 2, and if those don't work then you test 10, 11, 12, then 20, 21, 22. So if the test number ends in a 1, you've now cut down your trials by 70%.
For XXX2, the multiplier would have to end in 0, 1, 5, or 6. That removes 60%. You can continue for 3-9.

What about trying the other way?
I have solved this problem by generating numbers that are composed of 0, 1 and 2 from smallest to bigger and looking at whom this number is the multiple of. Moreover, I have used the pattern for 9, 99, etc. (btw, you don't need BigInteger other than them. Since you will precompute them, get rid of BigInteger) but I found their values like your bruteforce method by incrementing in the multiples of interested number as pointed out before. Result popped out in around 6 seconds. If you want to see my solution, here it is.

test is a string, so there's no need to call toString() on it. Not a big improvement, but it makes the code a little cleaner.
Just off the top of my head here's an algorithm that would avoid BigInteger altogether.
For each multiplier
Set carry = 0
Set multiple = ""
Iterate through the digits of the multiplier from right to left
Set temp = digit * n + carry
if (right-most digit of temp > 2)
break and go to next multiplier
else
Set carry = temp / 10 // drop last digit and carry result
This basically does a long multiplication with the opportunity to break out of it as soon as a digit > 2 is found. Note that, to solve the problem, we don't actually need to get F(n), just F(n)/n which is the first multiplier for which the above digit iteration completes. Then just sum the least multiplier for each n.
Without actually trying it out, i'm pretty sure that this will run with just int values.
Update
So I've been playing with some code, and got it to work for 1 <= n <= 100. Looks like the multiplier for n = 999 > 2^31, so we need to use at least 64-bit values. Not sure what the multiplier is yet. My code has been running in LinqPad for over 21 minutes and has passed 3.2*10^9 with no result yet. Of course, there could be a problem with my code.

Try keeping around the results as you go forward. You can use these to make lowerbound guesses for their multiples.
Suppose the acceptable LCM for some X is Y, with Y = RX. Then you know that Y is also a lowerbound for all {X, 2X, 3X, 4X, ... (R-1)X}.

Not sure if the following idea would help:
Let P(N, M) = digits of M base 10 are (<= 2) and M is a multiple of N.
Let G(N) = Some M such that P(N, M).
Let F(N) = M such that P(N, M) and M is minimal.
Important observation: F(N) <= G(N).
Would want G(N) to be "reasonable" in that it isn't gratuitously large and is easy to calculate. You might be able to caculate it effectively using F(N') on smaller components N' of N (digits of N perhaps).
Knowing G(N) might be extremely useful...
Perhaps you can work backwards with it.
Perhaps you can perform some sort of binary search with it.
If this technique is at all useful, I imagine the hard math would be to find G(N). The rest would probably be some clever computer science technique.

To reduce the number of iterations you may increment the trial product by 1 instead of incrementing the trial multiplier by 1. Then you check whether or not the trial product is divisible by f()'s argument. This will enable you to get to bigger values quicker because you can skip digit values 4 through 9 in the product when adding 1.
The below C code completes in under 5 minutes on a 2.4 GHz PC:
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <stddef.h>
#include <time.h>
typedef unsigned uint;
typedef unsigned char uint8;
typedef unsigned long long uint64;
uint64 f(uint n, uint64* multiplier)
{
uint8 carry, digits[20]; // 20 digits max for 64-bit values
uint digcnt = 1, i;
uint64 result;
assert(n > 0);
#if 0
// short cut:
//
// f(9) = 12222 = 9 * 1358
// f(99) = 1122222222 = 99 * 11335578
// f(999) = 111222222222222 = 999 * 111333555778
// f(9999) = 11112222222222222222 = 9999 * 1111333355557778
if (n == 9999)
{
*multiplier = 11112222222222222222ULL / 9999;
return 11112222222222222222ULL;
}
#endif
memset(digits, 0, sizeof(digits));
for (;;)
{
carry = 1;
for (i = 0; carry; i++)
{
assert(i < sizeof(digits));
carry += digits[i];
digits[i] = carry % 3;
carry /= 3;
}
if (i >= digcnt) digcnt = i;
result = 0;
for (i = 0; i < digcnt; i++)
result = result * 10 + digits[digcnt - 1 - i];
if (result % n == 0)
{
*multiplier = result / n;
break;
}
}
return result;
}
int main(void)
{
uint i;
uint64 sum = 0, product, multiplier;
time_t t;
char* p;
for (i = 1; i <= 10000; i++)
{
product = f(i, &multiplier);
printf("%s ", ((time(&t), p = ctime(&t)), p[strlen(p) - 1] = '\0', p));
printf("f(%u) = %llu = %u * %llu\n", i, product, i, multiplier);
sum += multiplier;
}
printf("%s ", ((time(&t), p = ctime(&t)), p[strlen(p) - 1] = '\0', p));
printf("sum(f(n)/n) = %llu\n", sum);
return 0;
}
Output:
Mon Jan 9 12:18:22 2012 f(1) = 1 = 1 * 1
Mon Jan 9 12:18:22 2012 f(2) = 2 = 2 * 1
Mon Jan 9 12:18:22 2012 f(3) = 12 = 3 * 4
Mon Jan 9 12:18:22 2012 f(4) = 12 = 4 * 3
Mon Jan 9 12:18:22 2012 f(5) = 10 = 5 * 2
Mon Jan 9 12:18:22 2012 f(6) = 12 = 6 * 2
Mon Jan 9 12:18:22 2012 f(7) = 21 = 7 * 3
Mon Jan 9 12:18:22 2012 f(8) = 112 = 8 * 14
Mon Jan 9 12:18:22 2012 f(9) = 12222 = 9 * 1358
...
Mon Jan 9 12:18:39 2012 f(9998) = 111122211112 = 9998 * 11114444
Mon Jan 9 12:22:50 2012 f(9999) = 11112222222222222222 = 9999 * 1111333355557778
Mon Jan 9 12:22:50 2012 f(10000) = 10000 = 10000 * 1
Mon Jan 9 12:22:50 2012 sum(f(n)/n) = 1111981904675169
If you change #if 0 to #if 1 and enable the short cut mentioned in a comment by Peter Lawrey, it will complete in just about 1 minute.

Here's my contribution for this interesting problem. Pardon me for using Java and BigInteger but based on my loose testing it wasn't such a blocker after all (calculating sum [1, 100] takes less than a second and sum [1, 10000] roughly 4,5mins on my 2.4 dual core). And out of those 4+ mins around 4mins are spent on f(9999). Quite a surprise.
import java.math.BigInteger;
public class Main {
public static void main(String args[]) {
BigInteger result = BigInteger.ZERO;
for (int i = 1; i <= 10000; ++i) {
BigInteger r = f(BigInteger.valueOf(i));
System.out.println("i=" + i + " r=" + r);
result = result.add(r.divide(BigInteger.valueOf(i)));
}
System.out.println("result=" + result);
}
// Find smallest x * value which consists only of numbers {0, 1, 2}.
private static BigInteger f(BigInteger value) {
BigInteger retVal = value;
while (!check(retVal)) {
BigInteger remainder = remainder(retVal);
BigInteger mult = remainder.subtract(retVal.remainder(remainder))
.divide(value);
retVal = retVal.add(value.multiply(mult.max(BigInteger.ONE)));
}
return retVal;
}
// Find highest remainder for given value so that value % retVal =
// XYYYYY.. Where X > 2 and 0 <= Y <= 9.
private static BigInteger remainder(BigInteger value) {
BigInteger curVal = BigInteger.TEN;
BigInteger retVal = BigInteger.TEN;
while (value.compareTo(BigInteger.TEN) >= 0) {
curVal = curVal.multiply(BigInteger.TEN);
value = value.divide(BigInteger.TEN);
if (value.remainder(BigInteger.TEN).intValue() > 2) {
retVal = curVal;
}
}
return retVal;
}
// Check if given value contains only 0, 1 and 2.
private static boolean check(BigInteger value) {
do {
if (value.remainder(BigInteger.TEN).intValue() > 2) {
return false;
}
value = value.divide(BigInteger.TEN);
} while (value.compareTo(BigInteger.ZERO) == 1);
return true;
}
}

Related

Performance issue with CountDiv (Codility) challenge algorithm

Needing some help with the algorithm i made to solve this codility challenge :
Write a function that, given three integers A, B and K, returns the number of integers within the range [A..B] that are divisible by K.
For example, for A = 6, B = 11 and K = 2, your function should return 3, because there are three numbers divisible by 2 within the range [6..11], namely 6, 8 and 10.
A and B are integers within the range [0..2,000,000,000];
K is an integer within the range [1..2,000,000,000];
A ≤ B.
public class Solution {
public int solution(int A, int B, int K) {
int counter = 0;
ArrayList<Integer> listOfNumbersInBetween = new ArrayList<>();
for (int i = A; i <= B; i++) {
listOfNumbersInBetween.add(i);
}
for (int arrayElement : listOfNumbersInBetween) {
if (arrayElement % K == 0) {
counter++;
}
}
return counter;
}}
As you can see, my solution works perfectly but performance wise it's scoring 0% due to the time complexity O(B-A).
How can i improve this code to make it score 100%?
Using a loop is brute-force, and challenges like this cannot be done with brute-force.
Which means you have to calculate the result. Challenges like this are more often a math question more than a programming question, so put you math hat on.
So think about it. In a range of integers, calculate how many are divisible by K. If I asked you to do this manually (using a simple calculator is allowed), without using a computer to brute-force it, how would you doing it? E.g. find how many integers between 111 and 999 that are divisible by 13
Hint
Find the first number in the range that is divisible by K, and the last number in the range that is divisible by K. For the example above, that would be 117 and 988.
Now calculate how many integers are divisible by K from first to last integer, remembering to count both of them. So, how many integers between 117 and 988 are divisible by 13?
Answer: (988 - 117) / 13 + 1 = 871 / 13 + 1 = 67 + 1 = 68
One possibility is to take advantage of integer arithmetic to get rid of some edge cases. Sometimes A and B are both, neither, or one or the other is divisible by k. And just subtracting them won't really help solve the problem. So one solution is to divide each by k before subtracting them.
Say k = 7, A = 12, and B = 54.
54/7 - 12/7 = 7 - 1 = 6 (14,21,28,35,42,49)
But what if A was 14?
54/7 - 14/7 = 7 - 2 = 5 (14,21,28,35,42,49) The answer is one off. So when A is divisible by k, 1 needs to be added.
What if A and B are both divisible by k?
56/7 - 14/7 = 8 - 2 = 6 = (14,21,28,34,42,49,56). The answer is again, one off, so the special case of A being divisible by k takes care of it by adding 1
int result = (B/k - A/k) + ((A%k == 0) ? 1 : 0);
My C# solution, based on #Andreas' brilliant one. This eventually got me to 100%. Most surprising (and perhaps wrong?) is that [0, 0, 11] needs to produce a result of 1, meaning that 0 is considered divisible by 11. You'll see I had to comment out an error catcher to allow B to be zero and get me to the "expected" answer. I was surprised that (0-0)/11 didn't produce a runtime error, but it didn't.
public int solutionCountDiv4(int A, int B, int K)
{
//Errors
if (K == 0)
return 0;
//if (B == 0)
// return 0;
if (A > B)
return 0;
var first = 0;
var last = 0;
for (first = A; first <= B; first++)
{
if (first % K == 0)
break;
}
for (last = B; last >= A; last--)
{
if (last % K == 0)
break;
}
if (first > last)
return 0;
var result = (last - first) / K + 1;
return result;
}
Small correction to #Ersin's solution
int solution(int A, int B, int K)
{
auto result = B / K - (A - 1) / K;
if (A == 0 and K > 1)
result++;
return result;
}

Get powers of a perfect power number

I have a problem which is to find the 2 powers of any number (numbers that don't have any powers such as 5 will return null), powers being and 2 integer numbers that when added power to return the said number. Here are some examples:
4 -> {2, 2}
5 -> null
6 -> null
7 -> null
8 -> {2, 3}
10 -> null
etc...
Although my code below works, however its too slow, when passed through the problem (about 100 integer.max values) it takes over the set time (16 seconds), anything I could to optimize this code?
public static int[] isPerfectPower(int n) {
int limit = (int)Math.round((n/((double)5/2)));
for (int i = 2; i <= limit; i++) {
double result = Math.pow(n, (double)1/i);
result = (double)Math.round(result * Math.pow(10, 10)) / Math.pow(10, 10);
if((result == Math.floor(result))) return new int[] {(int)result, i};
}
return null;
}
Your input is no more than 2147483647, which means there are only so many possible answers. Here is an ordered list of all 108 perfect powers with a power of 5 or more.
2**5, 2**7, 3**5, 4**5, 2**11, 3**7, 5**5, 6**5, 2**13, 4**7, 7**5, 8**5, 9**5, 5**7, 10**5, 2**17, 11**5, 3**11, 12**5, 6**7, 13**5, 2**19, 14**5, 15**5, 7**7, 16**5, 17**5, 3**13, 18**5, 8**7, 19**5, 20**5, 21**5, 4**11, 9**7, 22**5, 23**5, 24**5, 2**23, 25**5, 10**7, 26**5, 27**5, 28**5, 11**7, 29**5, 30**5, 31**5, 32**5, 12**7, 33**5, 34**5, 5**11, 35**5, 36**5, 13**7, 4**13, 37**5, 38**5, 39**5, 40**5, 14**7, 41**5, 3**17, 42**5, 43**5, 44**5, 15**7, 45**5, 46**5, 47**5, 48**5, 16**7, 49**5, 50**5, 51**5, 6**11, 52**5, 17**7, 53**5, 54**5, 55**5, 2**29, 56**5, 57**5, 18**7, 58**5, 59**5, 60**5, 61**5, 19**7, 62**5, 63**5, 64**5, 65**5, 3**19, 5**13, 66**5, 20**7, 67**5, 68**5, 69**5, 70**5, 21**7, 71**5, 72**5, 7**11, 73**5
Therefore you only have to check for squares, cubes, and entrees of the list above.
A slightly more naïve method would be to check all ten powers 2, 3, 5, 7, 11, 13, 17, 19, 23, and 29. You do not need to check any other powers, as they are either non-prime or too large to ever work.
You can do it by factoring a number.
Lets n = p1^k1 * p2^k2 * p3^k3 where p1,p2,p3 = prime number.
Then a number will be perfect power if gcd(k1, k2, k3) != 1 (They need to have common divisor)..
Example:
2500 = 2^2 * 5^4
= 2^2 * (5^2)^2
= 2^2 * 25^2
= 50^2
This way you can calculate power of perfect powers.
Way 2:
Lets n = a^b ... you need to find a & b where b < log(n)...
Now u need to find a.. you can find a using binary search. this complexity log(a)...to calculate a^b1..... u need log(n) operation.
So complexity for all binary operation: (log(n) * log log(n))
Total complexiy : log(n) * (log(n) * log log(n))
As #Mark Dickinson suggested, the most efficient change to my code ( without completely changing it ) would be to cap my limit at 30 instead of 2/3 of n, as any number >2 with a power greater then 30 would exceed the Integer.max limit, therefore but adding an extra expression ( i < 30 ) would immensely speed up the code, the code will be displayed below.
public static int[] isPerfectPower(int n) {
for(int i = 2; i <= ((n < 30) ? n : 30) && i < 30; i++) {
double result = (double)Math.round(Math.pow(n, (double)1/i) * Math.pow(10, 10)) / Math.pow(10, 10);
if((result == Math.floor(result))) return new int[] {(int)result, i};
}
return null;
}

How could we get within a certain range, the maximum number of times, an int is divisible by 2

I am doing the following programming exercise: Strongest even number in an interval. The statement is:
A strongness of an even number is the number of times we can
successively divide by 2 until we reach an odd number starting with an
even number n.
For example, if n = 12, then
12 / 2 = 6
6 / 2 = 3
we divided successively 2 times and we reached 3, so the strongness of
12 is 2.
If n = 16 then
16 / 2 = 8
8 / 2 = 4
4 / 2 = 2
2 / 2 = 1
we divided successively 4 times and we reached 1, so the strongness of
16 is 4 Task
Given a closed interval [n, m], return the even number that is the
strongest in the interval. If multiple solutions exist return the
smallest strongest even number.
Note that programs must run within the alloted server time; a naive
solution will probably time out. Constraints
1 <= n < m <= INT_MAX Examples
for the input [1, 2] return 2 (1 has strongness 0, 2 has strongness 1)
for the input [5, 10] return 8 (5, 7, 9 have strongness 0; 6, 10 have
strongness 1; 8 has strongness 2)
for the input [48, 56] return 48
First I thought to store in a map each number as a key, and the number of times it is divisible by 2, as a value:
import java.util.*;
public class StrongestEvenNumber {
public static int strongestEven/*💪*/(int n, int m) {
System.out.println("n: "+n);
System.out.println("m: "+m);
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
for(int i = n, number = 0, strongness = 0; i <= m; i++){
for(number = i, strongness = 0; number % 2 == 0; strongness++){
number /= 2;
}
map.put(i, strongness);
}
Map.Entry<Integer, Integer> maxEntry = null;
for(Map.Entry<Integer,Integer> entry : map.entrySet()){
if(maxEntry == null || entry.getValue().compareTo(maxEntry.getValue()) > 0){
maxEntry = entry;
}
}
return maxEntry.getKey();
}
}
However, with large numbers, it runs out of heap memory space, or execution time runs out. For example with:
n: 1180381085
m: 2074186600
Java heap space runs out.
And with:
n: 324243
m: 897653214
Execution time runs out. The execution time exceeds 16000 ms
Then I tried to just store the number which is the most times divisible by 2:
import java.util.*;
public class StrongestEvenNumber {
public static int strongestEven/*💪*/(int n, int m) {
System.out.println("n: "+n);
System.out.println("m: "+m);
int maxStrongness = 0, maxNumber = 0;
for(int i = n, number = 0, strongness = 0; i <= m; i++){
for(number = i, strongness = 0; number % 2 == 0; strongness++){
number /= 2;
}
if(strongness > maxStrongness){
maxStrongness = strongness;
maxNumber = i;
}
}
return maxNumber;
}
}
Indeed it solves the heap space difficulty, however the execution time runs out behaviour stills happening.
For example with:
n: 200275492
m: 1590463313
The execution time exceeds 16000 ms
I have also read:
Finding Key associated with max Value in a Java Map
Get the key for the maximum value in a HashMap using Collections
https://math.stackexchange.com/questions/2589831/how-many-times-can-i-divide-a-number-by-another
Number of times all the numbers in an array are divisible by 2
optimize code to get the number of integers within given range that are divisible by integer
Well, the strongness of a value x is n when x is represented as
x = k * 2**n
knowing this we can check all powers of 2 (i.e. 1, 2, 4, 8, ...) if we can find any k such that
from <= k * 2**n <= to
Code:
private static int strongestEven(int from, int to) {
if (to < from)
return -1; // Or throw exception
// best power of 2 we can insert between [to..from] as k * best
int best = 1;
while (true) {
int ceiling = from / best + (from % best == 0 ? 0 : 1);
int floor = to / best;
if (ceiling > floor) {
best = best / 2;
return best * (to / best);
}
best *= 2;
}
}
Tests:
[ 1, 2] => 2
[ 5, 10] => 8
[48, 56] => 48
[80, 100] => 96
[97, 100] => 100
Finally,
[1180381085, 1590463313] => 1342177280
we have 1342177280 == 5 * 268435456 == 5 * 2**28 so the strongest number within [1180381085, 1590463313] range has strongness 28
Please, note, that the algorithm has O(log(to)) time complexity that's why will do even if we turn all int into long
The strongness is actually the number of trailing zeros in the binary representation of the number. You can use the java.lang.Integer.numberOfTrailingZeros to get it.
And as you want to test the even numbers, you can skipp the odd numbers in your loop.
public class StrongestEvenNumber {
public static int strongestEven(int n, int m) {
int maxStrongness = 0, maxNumber = 0;
for(int i = n%2==0?n:n+1, strongness = 0; i <= m; i=i+2){
strongness = Integer.numberOfTrailingZeros(i);
if(strongness > maxStrongness){
maxStrongness = strongness;
maxNumber = i;
}
}
return maxNumber;
}
This runs in the allocated time:
Completed in 13190ms

How can I get the greatest product in an array of integers?

This is not for a homework. It's just a part of a monthly algorithm exam for engineers in our company. The exam is done. I failed and this was the part of the problem that stumped me. Though it is not required to pass, it has an effect on your yearly bonus on how well you do in these algorithm exams.
So, given an array of integers, find the greatest product you can get using its elements. This is not the actual problem, just a tiny part of it.
The constraints are:
3 <= size_of_array <= 8
1 <= integer_in_array <= 9
Libraries are not allowed (e.g. Integer.max, Arrays.sort, etc...)
The initial solution I've thought of was to sort the array in descending order. Take the greatest value in the array then multiply it to the remaining integers.
Suppose, we have an array:
int[] arr = {3, 6, 5, 4};
sort(arr); // array becomes {6, 5, 4, 3}
int product = getProduct(arr);
In the getProduct method, I have:
int getProduct(int[] arr) {
int maxVal = arr[0];
int multiplier = 0;
for(int i = 0; i < arr.length; i++) {
if(multiplier == 0) {
multiplier = arr[i];
} else {
multiplier *= 10;
multiplier += arr[i];
}
}
return maxVal * multiplier; // 6 * 543 = 3258
}
It turns out the my implementation is not correct because the highest product you can get out of this array would be:
63 * 54 = 3402
I think this part of the problem boils down to some sort of permutation problem where you try all possible combinations. Please note that libraries are not allowed. So in case you need some type of sorting, you have to implement it yourself. I would appreciate if the answer is in Java since it is my language of choice for this problem.
EDIT:
The value of each element in the array is always a positive single digit integer except for zero. Hence the constraint,
1 <= integer_in_array <= 9
Each integer could vary from 1 to 9 only. And the number of elements in the array varies from 3 to 8 only. The smallest array you could get would have 3 elements and the biggest would have 8 elements in it.
The question is how to get the highest possible product using the elements in the array. So, in my sample array, possible combinations would be:
63 * 54 = 3402 // Correct answer
6 * 543 = 3258 // My answer (wrong)
53 * 46 = 2438
4 * 536 = 2144
36 * 54 = 1944
5 * 634 = 3170
Easy way: brute force to all combinations. Let's divide the array into 2 parts, so we want to create two largest number from those numbers.
public int maxProduct(int[]data) {
Arrays.sort(data);//non-decreasing order
int n = data.length;
int result = 0;
for(int i = 0; i < (1 << n); i++){
int a = 0;
int b = 0;
for(int j = n - 1; j >= 0; j--){
if(((1 << j) & i) == 0){
a = a * 10 + data[j];
}else{
b = b * 10 + data[j];
}
}
result = Integer.max(result, a*b);
}
return result;
}
Time complexity : O(n*2 ^ n)
Here’s my go. I have no strict argument that it works correctly in all cases, nor that it’s the simplest possible solution. On the other hand I haven’t found any holes yet.
static int getProduct(int... arr) {
// Should validate arr
Arrays.sort(arr);
int a = arr[arr.length - 1];
int b = arr[arr.length - 2];
for (int ix = arr.length - 3; ix >= 0; ix--) {
int digit = arr[ix];
// append d to either a or b depending on where it makes the greater product
int candidate1 = (a * 10 + digit) * b;
int candidate2 = a * (b * 10 + digit);
if (candidate1 > candidate2) {
a = a * 10 + digit;
} else {
b = b * 10 + digit;
}
}
System.out.println("" + a + " * " + b + " = " + a * b);
return a * b;
}
For testing purposes I have put in a System.out.println that you may not want to be there in your final method. Some example calls:
getProduct(9, 8, 1);
getProduct(9, 2, 1);
getProduct(1, 2, 2, 1);
getProduct(1, 2, 3, 9);
getProduct(1, 2, 8, 9);
getProduct(1, 7, 8, 9);
getProduct(2, 3, 4, 5, 6);
getProduct(1, 3, 3, 4, 6, 7, 7, 9);
Output from same:
9 * 81 = 729
9 * 21 = 189
21 * 21 = 441
91 * 32 = 2912
91 * 82 = 7462
91 * 87 = 7917
63 * 542 = 34146
9631 * 7743 = 74572833
Please start shooting…
Edit: I think that a is always constructed from the digits at indices length - 1, length - 4, length - 6, length - 8, and b from length - 2, length - 3, length - 5, length - 7, but limitied to the length of the array, of course. This observation may lead to code that is simpler (and performs a bit more efficiently), but where it’s even less obvious that it gives the best result.

Random - nextInt(int y) isn't able to give me 18 even integers in a row when 'int y' % 2 == 0 && 'int y' != a power of 2

sorry for that title but I wanted to pack as much information about my problem in as little space as possible without being too confusing.
So, I have a loop which runs n times and each time it uses a = r.nextInt(int y); to generate an int and if all n integers generated are even numbers, then the program "returns true".
The weird thing is: if I chose n to be 18 or higher while y is and even number which is not a power of 2, then the programm will not "termintate successfully".
I love to help you help me, and can take a heavy dose of criticism.
(I know I'm asking about the Random/nextInt(int) topic but I will also take tips for better coding)
EDIT: I looked into the Documentation for Java8 befor I posted here and for powers of two the method uses a different way of producing the random number.
What I don't understand is why is 18 the breakpoint for consecutive even numbers and why does it work with odd numbers for nextInt(int)?
So the following code will work with howManyInts = 16 or 17 but not 18 (or higher) when nextIntValue is an even number which is not a power of two (6,10,12...)
It works with howManyInts = 25 and nextIntValue = 8 in less than 20 seconds
import java.util.*;
class test{
public static void main(String[] args) {
boolean win = false;
int areEven = 0;
long loopCounter = 0; // The loopCounter is used to control the maximum number of loops should be run incase the loop is endless
int howManyInts = 18;
int nextIntValue = 6; // nextIntValue = 6 or 10 won't work while all powers of 2 work fine
// also, I don't want an odd value as that would change to odds towards odd values...
while(win == false){
loopCounter += 1;
areEven = 0;
Random r = new Random();
int[] num = new int[howManyInts];
for(int a = 0; a < num.length; a++){
num[a] = r.nextInt(nextIntValue);
if(num[a] % 2 == 0){
areEven += 1;
}
}
if(areEven == num.length || loopCounter >= 10000000){
win = true;
System.out.println("It took " + loopCounter + " loops to get " + num.length + " random values which are all even.");
}
}
}
}
If you use SecureRandom instead of Random, your program will finish fairly quickly.
Another way would be to use nextDouble instead
num[a] = (int) (r.nextDouble() * nextIntValue);
The problem with Random.nextInt(int n) is I believe hidden within its implementation and you can read about it in its javadoc.
The algorithm is slightly tricky. It rejects values that would result
in an uneven distribution (due to the fact that 2^31 is not divisible
by n). The probability of a value being rejected depends on n. The
worst case is n=2^30+1, for which the probability of a reject is 1/2,
and the expected number of iterations before the loop terminates is 2.
The algorithm treats the case where n is a power of two specially: it
returns the correct number of high-order bits from the underlying
pseudo-random number generator. In the absence of special treatment,
the correct number of low-order bits would be returned. Linear
congruential pseudo-random number generators such as the one
implemented by this class are known to have short periods in the
sequence of values of their low-order bits. Thus, this special case
greatly increases the length of the sequence of values returned by
successive calls to this method if n is a small power of two.
The implementation looks like this:
public int nextInt(int n) {
if (n <= 0)
throw new IllegalArgumentException("n must be positive");
if ((n & -n) == n) // i.e., n is a power of 2
return (int)((n * (long)next(31)) >> 31);
int bits, val;
do {
bits = next(31);
val = bits % n;
} while (bits - val + (n-1) < 0);
return val;
}
While the next method looks like this (I've replaced the constants with literals)
protected int next(int bits) {
long oldseed, nextseed;
AtomicLong seed = this.seed;
do {
oldseed = seed.get();
nextseed = (oldseed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1);
} while (!seed.compareAndSet(oldseed, nextseed));
return (int)(nextseed >>> (48 - bits));
}
(I suppose that 48-31 == 17 is purely coincidental)
Interesting question!
I have added some statistic-gathering to the code:
import java.util.*;
public class J {
static Random r = new Random();
private static class Stats {
long s[];
public Stats(int n) { this.s = new long[n]; }
public String toString() {
return Arrays.toString(s);
}
}
public static void test(int target, int options) {
boolean win = false;
Stats s = new Stats(target);
for (long iterations = 0; !win; iterations ++) {
int even = 0;
for (int i = 0; i < target; i++) {
if ((r.nextInt(options) % 2) != 0) {
s.s[i] ++;
break;
} else {
even ++;
}
}
if (even == target) {
win = true;
System.out.println(
"It took " + iterations + " loops to get " + target
+ " random values which are all even. Stats: " + s);
} else if (iterations >= 1E8) {
win = true;
System.out.println(iterations + ": " + s);
}
}
}
public static void main(String args[]) {
test(Integer.parseInt(args[0]), Integer.parseInt(args[1]));
}
}
The code now ends if no sequence is found after 100M tries; and always stores how far it managed to get before failing (= drawing an odd number).
These are some runs:
18 9: It took 57235 loops to get 18 random values which are all even. Stats: [25401, 14081, 7864, 4328, 2508, 1366, 747, 390, 263, 126, 76, 38, 28, 4, 8, 4, 2, 1]
18 8: It took 48612 loops to get 18 random values which are all even. Stats: [24285, 12336, 6066, 2981, 1436, 738, 385, 197, 95, 43, 23, 10, 8, 7, 1, 0, 0, 1]
18 7: It took 23302 loops to get 18 random values which are all even. Stats: [10062, 5712, 3174, 1877, 1101, 590, 331, 190, 98, 59, 44, 31, 18, 8, 5, 2, 0, 0]
18 6: Aborted after 100000000 tries: [49997688, 24993911, 12503043, 6272129, 3113557, 1544194, 788879, 393680, 205236, 89264, 45016, 35858, 5340, 9155, 763, 1525, 0, 763]
So, for those particular values (100M attempts at runs of 18 even numbers, throwing 6-sided dice), there were 0 cases where the run bailed out because of the 17th number, but 763(!) where it bailed out because of the last number!
It definitely looks like a higher-quality PRNG is needed, such as the one mentioned by #radoh.
Probabilistically speaking, you would expect to find runs of N even throws of a fair coin with probability 1/(2^N); and you would expect to collect stats where each entry would be 1/2 the previous one. Encountering 0, 763 indicates a strong bias.
The issue is almost certainly with r.nextInt(nextIntValue);. Here you are requesting a random integer between 0 and 5. I cannot understand specifically why 18 is the break point but the chances a sequence of random positive integers less than a small number to be all even must reduce as the limit reduces.
I note that increasing that value from 6 to 100 still does not find length-18 even sequences. Perhaps the algorithm behind the scenes somehow influences the statistics.
Seems like the random generator doesn't allow 18 consecutive even numbers, when your maxRandom is even, starting at 6.
I changed the code a bit to demonstrate how the 18th random is always odd:
class NextIntWeirdness {
public static void main(String[] args) {
int maxRandom = 6;
Random r = new Random();
for (int i = 0; i < 100; i++) {
int evenNumbers = 17;
int evenResults;
do {
evenResults = 0;
for (int j = 0; j < evenNumbers; j++) {
int num = r.nextInt(maxRandom);
if (num % 2 != 0) {
break;
} else {
evenResults++;
}
}
} while (evenResults < evenNumbers);
System.out.println(r.nextInt(maxRandom));
}
}
}

Categories

Resources