Problem 20
Factorial digit sum
n! means n × (n − 1) × … × 3 × 2 × 1
For example, 10! = 10 × 9 × … × 3 × 2 × 1 = 3628800, and the sum of the digits in the number 10! is 3 + 6 + 2 + 8 + 8 + 0 + 0 = 27.
Find the sum of the digits in the number 100!
阶乘数字和
n! 的意思是 n × (n − 1) × … × 3 × 2 × 1
例如,10! = 10 × 9 × … × 3 × 2 × 1 = 3628800,所以10!的各位数字和是 3 + 6 + 2 + 8 + 8 + 0 + 0 = 27。
求出100!的各位数字和。
package projecteuler;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.junit.Test;
public class Prj20 {
/**
* n! means n × (n − 1) × ... × 3 × 2 × 1
*
* For example, 10! = 10 × 9 × ... × 3 × 2 × 1 = 3628800, and the sum of the
* digits in the number 10! is 3 + 6 + 2 + 8 + 8 + 0 + 0 = 27.
*
* Find the sum of the digits in the number 100!
*/
@Test
public void test() {
System.out.println( Calculator.calculate(100));
}
public static class Calculator {
/**
* 1. 数组存取 2. 分解因子部分优化 2 * 5 = 10 , 不影响计算的去除 3. 乘法进一位
*
* @param n
* @return
*/
public static int calculate(int n) {
int bits = getBits(n);
int[] data = new int[bits];
data[bits - 1] = 1; // init
Map<Long, Integer> primeMap = new HashMap<Long, Integer>();
IntegerDivisor div = new IntegerDivisor();
for (int i = 2; i <= n; i++) {
div.divisor(i);
mergerPrimeMap(div.primeMap, primeMap);
div.clear();
}
optimizeMap(primeMap);
System.out.println( IntegerDivisor.print_Map(primeMap));
for (Entry<Long, Integer> entry : primeMap.entrySet()) {
Long prime = entry.getKey();
int times = entry.getValue();
for (int i = 1; i <= times; i++) {
for (int bitIter = 0; bitIter < bits; bitIter++) {
data[bitIter] *= prime;
}
for (int bitIter = bits - 1; bitIter > 0; bitIter--) {
data[bitIter - 1] += (data[bitIter] / 10);
data[bitIter] %= 10;
}
}
}
int sum = 0;
for (int i = 0; i < bits; i++) {
sum += data[i];
System.out.println("data[" + i +"]=" + data[i]);
}
return sum;
}
private static void mergerPrimeMap(Map<Long, Integer> divPrimeMap,
Map<Long, Integer> primeMap) {
for( Entry<Long, Integer> entry : divPrimeMap.entrySet()){
if( !primeMap.containsKey(entry.getKey())){
primeMap.put( entry.getKey(), 0);
}
int time = primeMap.get( entry.getKey()) + entry.getValue();
primeMap.put( entry.getKey(), time);
}
}
private static void optimizeMap(Map<Long, Integer> primeMap){
if( primeMap.containsKey(2L) && primeMap.containsKey(5L)){
int minSubtract = Math.min( primeMap.get(2L), primeMap.get(5L));
int time2 = primeMap.get(2L);
int time5 = primeMap.get(5L);
primeMap.put((long) 2, time2 - minSubtract);
primeMap.put((long) 5, time5 - minSubtract);
if( primeMap.get(2L) == 0){
primeMap.remove(2L);
}
if( primeMap.get(5L) == 0){
primeMap.remove(5L);
}
}
}
public static int getBits(int n) {
double bits = 0;
for (int i = 1; i <= 100; i++) {
bits += Math.log(i) / Math.log(2);
}
System.out.println(bits);
return (int) (Math.ceil(bits) + 1);
}
}
public static class IntegerDivisor {
public Map<Long, Integer> primeMap = new HashMap<Long, Integer>();
public List<Long> primeList = new ArrayList<Long>();
public void clear() {
primeMap.clear();
primeList.clear();
}
public void divisor(long num) {
if (num <= 1)
return;
long prime = getPrime(
num,
primeList.size() == 0 ? 2
: primeList.get(primeList.size() - 1));
if (prime < 0) {
primeMap.put(num, 1);
primeList.add(num);
return;
} else {
primeList.add(prime);
int count = 0;
do {
count += 1;
num = num / prime;
} while (num % prime == 0);
primeMap.put(prime, count);
divisor(num);
}
}
private long getPrime(long num, long start) {
for (long i = start; i <= Math.sqrt(num); i++) {
if (num % i == 0) {
return i;
}
}
return -1;
}
@Override
public String toString() {
return print_Map( this.primeMap);
}
public static String print_Map(Map<Long, Integer> primeMap) {
StringBuilder sb = new StringBuilder();
for (Entry<Long, Integer> entry : primeMap.entrySet()) {
sb.append(entry.getKey().toString() + "="
+ entry.getValue().toString() + "\n");
}
return sb.toString();
}
public Long getLargestPrime() {
return primeList.get(primeList.size() - 1);
}
}
}