Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting prime factors in functional Java streams with a single method?

This method will take in a Long and return a LongStream of prime numbers for any digit passed to the method.

factors.java

public LongStream factors(long x){
  LongStream factorStream = LongStream.range(1, x+1).filter(n -> x%n == 0);
  return factorStream;
}

Utilizing the above method to find common factors first is ok.

primeFactors.java

public LongStream primeFactors(long x){
  LongStream primeFactorStream = factors(x).filter(n -> factors(n).count() == 0); 
  //doesn't work as factors.java returns a LongStream, which might include non-prime factors, which will not equate to zero.
  return primeFactorStream;
}

I understand this should be easily circumvented with the use of a simple isPrime() method with a predicate, but is there a way to do the same thing for for prime factors but only with a single method?

like image 826
Carrein Avatar asked Oct 16 '17 16:10

Carrein


3 Answers

If you want to do it in a single method without the aid of an existing test-for-prime method, you can do it like

public static LongStream primeFactors(long x) {
    return LongStream.rangeClosed(2, x)
                     .filter(n -> x % n == 0)
                     .filter(n -> LongStream.rangeClosed(2, n/2).noneMatch(i -> n%i==0));
}

You can test the method like

IntStream.concat(IntStream.rangeClosed(2, 15), IntStream.rangeClosed(90, 110))
         .forEach(number -> System.out.printf("%3d = %s%n", number,
            primeFactors(number)
                .mapToObj(d -> {
                    int p = 0;
                    for(long l = number; l%d == 0; l /= d, p++) l++;
                    return p == 1? String.valueOf(d): d + "^" + p;
                })
                .collect(Collectors.joining(" * ")))
         );
}
  2 = 2
  3 = 3
  4 = 2^2
  5 = 5
  6 = 2 * 3
  7 = 7
  8 = 2^3
  9 = 3^2
 10 = 2 * 5
 11 = 11
 12 = 2^2 * 3
 13 = 13
 14 = 2 * 7
 15 = 3 * 5
 90 = 2 * 3^2 * 5
 91 = 7 * 13
 92 = 2^2 * 23
 93 = 3 * 31
 94 = 2 * 47
 95 = 5 * 19
 96 = 2^5 * 3
 97 = 97
 98 = 2 * 7^2
 99 = 3^2 * 11
100 = 2^2 * 5^2
101 = 101
102 = 2 * 3 * 17
103 = 103
104 = 2^3 * 13
105 = 3 * 5 * 7
106 = 2 * 53
107 = 107
108 = 2^2 * 3^3
109 = 109
110 = 2 * 5 * 11

Needless to say, this isn’t the most efficient approach…

like image 80
Holger Avatar answered Nov 12 '22 03:11

Holger


You could make use of the BigInteger's isProbablePrime() method to check if your factors are prime:

public static LongStream primeFactors(long x){
    LongStream primeFactorStream = factors(x)
            .filter(n -> new BigInteger(String.valueOf(n)).isProbablePrime(10));
    return primeFactorStream;
}

For primeFactors(26).forEach(System.out::println); it returns 2 13.

like image 39
Schidu Luca Avatar answered Nov 12 '22 02:11

Schidu Luca


Without memoization, and using LongStream you could apply few simple performance improvements like for stream of prime factors producing a stream of numbers up to x/2:

public static LongStream factors(long x){
  return LongStream.rangeClosed(2, x/2).filter(n -> x % n == 0);
}

public static LongStream primeFactors(long x){
  return LongStream.rangeClosed(2, x/2)
    .filter(n -> x % n == 0).filter(n -> factors(n).count() == 0);
}

Which for very large x will matter. However this solution repeats the test of x % n == 0 for each n in each of 2 streams, which begs for memoization.

like image 42
diginoise Avatar answered Nov 12 '22 03:11

diginoise