I want to replace BigInteger for loop in Stream Api - java

Want to replace BigInteger loop in Java Stream API.
Below code, I have to modify in java8 using Stream API. Because of performance concerns.
My question what would be the best practice to change below code into best performance.
public BigInteger getSum(BigInteger n, BigInteger c) {
BigInteger sum = BigInteger.ZERO;
for (BigInteger i = BigInteger.ZERO; i.compareTo(n) < 0; i=i.add(BigInteger.ONE)) {
sum = sum.add(calculateProduct(i, i.subtract(BigInteger.ONE), c));
}
return sum;
}
private BigInteger calculateProduct(BigInteger i, BigInteger j, BigInteger c) {
if (j.compareTo(BigInteger.ZERO) < 0) return BigInteger.ZERO;
if (j.compareTo(BigInteger.ZERO) == 0) return BigInteger.ONE;
if ((i.subtract(j).compareTo(c)) <= 0){
return j.add(BigInteger.ONE).multiply(calculateProduct(i, j.subtract(BigInteger.ONE), c));
}else
return BigInteger.ONE;
}

It looks like you are adding up products of sliding window of width c. If you want to improve performance, get rid of recursion and avoid recomputing the entire product. Use the product computed in the previous step: multiply it by the number entering the window and divide by the number exiting the window. Division is slower but it will pay off for larger values of c.
Finally, while I will keep your method signature, you really only need BigInteger as return.
BigInteger getSum(BigInteger n, BigInteger c) {
BigInteger p = BigInteger.ONE, sum = BigInteger.ZERO;
for (BigInteger x = BigInteger.ONE; x.compareTo(n) < 0; x = x.add(BigInteger.ONE)) {
p = p.multiply(x);
if (x.compareTo(c) > 0) {
p = p.divide(x.subtract(c));
}
sum = sum.add(p);
}
return sum;
}
If you want to speed it up further, you can use a bit of math to avoid having to divide at every step. Your sum can be broken up into s1 = sum[1,c) x! and s2 = sum[c,n) x!/(x-c)!. The second sum equals n!/(n-c-1)!/(c+1) (follows from hockey stick identity). The method below does not handle the trivial case of c >=n. I will leave that to you.
private static BigInteger fasterSum(BigInteger n, BigInteger c) {
assert c.compareTo(n) < 0;
BigInteger sum = ZERO, p = ONE;
for (BigInteger x = ONE; x.compareTo(c) < 0; x = x.add(ONE)) {
p = p.multiply(x);
sum = sum.add(p);
}
// sum[c,n) x!/(x-c)! = n!/(n-c-1)!/(c+1)
p = ONE;
for (BigInteger x = n.subtract(c); x.compareTo(n) <= 0; x = x.add(ONE)) {
p = p.multiply(x);
}
sum = sum.add(p.divide(c.add(ONE)));
return sum;
}

So I assume you want to add a list of BigInteger:
You can do that by using a reduce operation (https://docs.oracle.com/javase/tutorial/collections/streams/reduction.html)
List<BigInteger> list = new ArrayList<>();
list.add(BigInteger.valueOf(5));
list.add(BigInteger.valueOf(1));
list.add(BigInteger.valueOf(3));
list.add(BigInteger.valueOf(10));
list.add(BigInteger.valueOf(2));
BigInteger sum = list.stream().reduce((x, y) -> x.add(y)).get();
System.out.println(sum);
That will calculate the sum, an equivalent would be:
BigInteger sum = list.stream().reduce(BigInteger::add).get();
You could also write an own Collector that is reusable and extract the reduce operation to there:
public static Collector<BigInteger, ?, BigInteger> calculateSum() {
return Collectors.reducing(BigInteger.ZERO, BigInteger::add);
}
and then do:
BigInteger sum = list.stream().collect(calculateSum());

Related

Fibonacci sequence - How to calculate the sum of the first 100 even-values Fibonacci numbers?

Fibonacci sequence is defined as a sequence of integers starting with 1 and 1, where each subsequent value is the sum of the preceding two I.e.
f(0) = 1
f(1) = 1
f(n) = f(n-1) + f(n-2) where n>=2
My goal is to calculate the sum of the first 100 even-values Fibonacci numbers.
So far I've found this code which works perfectly to calculate the sum of even numbers to 4million , however I'm unable to find edit the code so that it stops at the sum of the 100th value, rather than reaching 4million.
public class Improvement {
public static int Fibonacci(int j) {
/**
*
* Recursive took a long time so continued with iterative
*
* Complexity is n squared.. try to improve to just n
*
*/
int tmp;
int a = 2;
int b = 1;
int total = 0;
do {
if(isEven(a)) total +=a;
tmp = a + b;
b = a;
a = tmp;
} while (a < j);
return total;
}
private static boolean isEven(int a) {
return (a & 1) == 0;
}
public static void main(String[] args) {
// Notice there is no more loop here
System.out.println(Fibonacci(4_000_000));
}
}
Just to show the console from #mr1554 code answer, the first 100 even values are shown and then the sum of all is 4850741640 as can be seen below:
Any help is appreciated, thanks!
You need to use BigInteger because long easily overflows as Fibonacci's scales quite easily. BigInteger is also tricky to check whether is an odd or even number, but you can use BigInteger::testBit returning boolean as explained in this answer.
Here is some complete code:
BigInteger fibonacciSum(int count, boolean isOdd) {
int i = 0;
BigInteger sum = BigInteger.ZERO;
BigInteger current = BigInteger.ONE;
BigInteger next = BigInteger.ONE;
BigInteger temp;
while (i < count) {
temp = current;
current = current.add(next);
next = temp;
if ((current.testBit(0) && isOdd) || ((!current.testBit(0) && !isOdd))) {
sum = sum.add(current);
i++;
}
}
return sum;
}
Or you can have some fun with Stream API:
BigInteger fibonacciSum(int count, boolean isOdd) {
final BigInteger[] firstSecond = new BigInteger[] {BigInteger.ONE, BigInteger.ONE};
return Stream.iterate(
firstSecond,
num -> new BigInteger[] { num[1], num[0].add(num[1]) })
.filter(pair ->
(pair[1].testBit(0) && isOdd) ||
(!pair[1].testBit(0) && !isOdd))
.limit(count)
.map(pair -> pair[1])
.reduce(BigInteger.ZERO, BigInteger::add);
}
In any way, don't forget to test it out:
#Test
void test() {
assertThat(
fibonacciSum(100, false),
is(new BigInteger("290905784918002003245752779317049533129517076702883498623284700")));
}
You said.
My goal is to calculate the sum of the first 100 even-values Fibonacci numbers.
That number gets very large very quickly. You need to:
use BigInteger
use the mod function to determine if even
For this I could have started from (1,1) but it's only one term so ...
BigInteger m = BigInteger.ZERO;
BigInteger n = BigInteger.ONE;
BigInteger sumOfEven= BigInteger.ZERO;
int count = 0;
BigInteger t;
while( count < 100) {
t = n.add(m);
// check if even
if (t.mod(BigInteger.TWO).equals(BigInteger.ZERO)) {
sumOfEven = sumOfEven.add(t);
count++;
}
n = m;
m = t;
}
System.out.println(sumOfEven);
Prints
290905784918002003245752779317049533129517076702883498623284700
If, on the other hand, from your comment.
My aim is to calculate the sum of the first 100 even numbers
Then you can do that like so
sumFirstNeven = (((2N + 2)N)/2 = (N+1)N
so (101)100 = 10100 and the complexity is O(1)
as I figured, you want a program to sum 100 first even values of the Fibonacci series.
here is a sample code, when you run the program it will ask you to determine the number of the even values, you want 100 value e.g, type 100 in consul:
public static void main(String[] args) {
int firstNumber = 0;
int secondNumber = 2;
System.out.print("Enter the number of odd elements of the Fibonacci Series to sum : ");
Scanner scan = new Scanner(System.in);
int elementCount = scan.nextInt(); // number of even values you want
System.out.print(firstNumber + ", ");
System.out.print(secondNumber + ", ");
long sum = 2;
for (int i = 2; i < elementCount; i++) {
int nextNumber = firstNumber + secondNumber;
System.out.print(nextNumber + ", ");
sum += (nextNumber);
firstNumber = secondNumber;
secondNumber = nextNumber;
}
System.out.print("...");
System.out.println("\n" + "the sum of " + elementCount + " values of fibonacci series is: " + sum);
}

Something wrong with BigInteger

public static BigInteger OtherToDecimal(String value, int base) {
BigInteger sum = new BigInteger("0");
String kt = "0123456789ABCDEF";//
for (int i = 0; i < value.length(); i++) {
BigInteger k = BigDecimal.valueOf(pow(base, value.length() - 1 - i)).toBigInteger();
sum = sum.add((BigInteger.valueOf(kt.indexOf(value.charAt(i))).multiply(k)));
}
return sum;
}
when i test this function with base16 :F0F0F0F0F0F0F0, it return right result = 67818912035696880
BUT when i test with base16: F0F0F0F0F0F0F0F0, it returns wrong result: 17361641481138401580 which right result must be 17361641481138401520
please help me!
Math.pow delivers a double, 8 bytes. So from some huge double value, it becomes imprecise in the less significant digits.
You could have used
new BigInteger(value, base)
The repair is:
BigInteger k = BigDecimal.valueOf(base).pow(value.length() - 1 - i));

How to keep increasing the value of a BigInteger?

I have a series of BigInteger problems that require the use of constantly increasing a BigInteger. I proposed a loop, but that is extremely tricky due to the fact that BigIntegers and BigDecimals are immutable.
Here is an example of one of the programs I am trying to make. It is a method trying to find BigIntegers larger than Long.MAX_VALUE that are divisible by 2 or 3.
public void divisibleBy2Or3() {
BigInteger min = new BigInteger("9223372036854775808");
int j = 0;
BigInteger increment = new BigInteger("1");
BigInteger divideBy2 = new BigInteger("2");
BigInteger divideBy3 = new BigInteger("3");
while (j < 10) {
BigInteger a = min.add(increment);
BigInteger b = a.divide(divideBy2); BigInteger c = a.divide(divideBy3);
if (b.multiply(divideBy2) == a || c.multiply(divideBy3) == a) {
System.out.print(a + " ");
j++;
}
}
}
The problem with this code is I can't seem to figure out how to get the BigInteger that I am testing for each iteration of the loop to add itself with each iteration. I'm also a little unsure as to if the multiply methods actually work for this scenario, as whenever I run the program, it hangs and shows a blank console
You need to keep track of your current value using a variable declared outside the loop - otherwise it keeps going back to min + 1.
static final BigInteger ONE = BigInteger.ONE;
static final BigInteger TWO = ONE.add(ONE);
static final BigInteger THREE = TWO.add(ONE);
public void divisibleBy2Or3() {
BigInteger min = new BigInteger("9223372036854775808");
int j = 0;
// Add this.
BigInteger value = min;
while (j < 10) {
value = value.add(ONE);
BigInteger b = value.divide(TWO);
BigInteger c = value.divide(THREE);
if (b.multiply(TWO).equals(value) || c.multiply(THREE).equals(value)) {
System.out.print(value + " ");
j++;
}
}
}

Usage of BigIntegers and to the power off i

I found out longs wont cut it, as the numbers I'm calculating are to huge to fit. I'm struggling with the concept of BigInts.
Lets say I have the following equation to perform.
int p = 11549
int n = 252817
The equation is as follows.. : ( number * p )^ (to the power of i)%n .
With longs I just did :
long number;
long p;
long n;
long temp;
long total;
for (int i=0; i<6;i++) {
temp = numer*Math.pow(p,i);
total += temp;
}
total %= n;
But when i use Math.pow on I the numbers get to huge to use this method, and I need to use BigIntegers. I Just dont understand how I can do it. Right now I got this : (Missing the % until i can figure out the power off statement.)
long temp;
long p;
BigInteger opphoyd;
BigInteger mod;
for (int i=0;i<6;i++) {
temp = number * p;
opphoyd = BigInteger.valueOf(temp);
mod = BigInteger.valueOf(i);
mod.add(opphoyd.pow(i));
mod.add(opphoyd);
System.out.println(mod);
}
But its not working at all, could anyone point me in the right direction?
BigInteger's add method (and most of the other methods) does not modify the BigInteger it is called on. Instead it returns a new BigInteger.
So you would need to do:
BigInteger sum = mod.add(opphoyd);
Take a look at the javadocs, it will be very helpful when working with BigInteger.
long temp;
long p;
BigInteger opphoyd;
BigInteger mod;
for( int i = 0; i < 6; i++ ) {
temp = number * p;
opphoyd = BigInteger.valueOf(temp);
mod = BigInteger.valueOf(i);
BigInteger sum = mod.add( opphoyd.pow(i));
sum = sum.add(opphoyd);
System.out.println( sum );
}

Using BigInteger in for-loop condition is not working. What should I do?

Another BigInteger problem. My code works for int and long, but since the test cases in UVa are larger, I need to use BigInteger. But I don't know how to use BigInteger and it drives me nuts! The code doesn't even enter the for-loop. It seems to be stuck in the condition part.
I tried using for or while, and they have the same problem.
public class Main{
public static void main(String[] asdf){
Scanner pp = new Scanner(System.in);
int testCases = pp.nextInt();
while(testCases-- > 0){
//BigInteger a = pp.nextBigInteger();
BigInteger low = pp.nextBigInteger();
BigInteger upp = pp.nextBigInteger();
BigInteger max = BigInteger.ZERO;
BigInteger i = low;
//((i.compareTo(upp)==-1)||
//(i.compareTo(upp)==0));
//i.add(BigInteger.ONE))
while((i.compareTo(upp))<0){
if(divCount(i).compareTo(divCount(max))==1){
max = i;
}
i.add(BigInteger.ONE);
}
System.out.println("Between "+low+" and "+upp+", "+max+" has a maximum of "+divCount(max)+" divisors.");
}
}
public static BigInteger divCount(BigInteger n){
BigInteger lim = n;
BigInteger size = BigInteger.ZERO;
BigInteger i = BigInteger.ONE;
while(i.compareTo(lim)<0){
if((n.mod(i).compareTo(BigInteger.ZERO))==0){
lim = n.divide(i);
if(!(lim.equals(i))){
size.add(BigInteger.ONE);
}
size.add(BigInteger.ONE);
}
i.add(BigInteger.ONE);
}
//return size;
return BigInteger.ONE;
}
}
i.add(BigInteger.ONE) does not modify i. Instead it returns a new object. Assign the value to i to get the effect you want. Same holds true for the other similar calls in your code.

Categories

Resources