根据号码X
,找到号码A
和B
,以便X = A^B
和A
是最小的号码。 B
小于301。
String[] power2(String X) {
double n = Double.parseDouble(X);
double i = 1;
double j = 1;
String[] result = {"1", "1"};
boolean flag = false;
if (n > 1) {
for (i = 1; i < n; i++) {
for (j = 1; j < 301; j++) {
if (Math.pow(i, j) == n) {
flag = true;
break;
}
}
if (flag) {
break;
}
}
}
result[0] = i + "";
result[1] = j + "";
return result;
}
这种方法很好但效率不高。我正在寻找更快的算法。
答案 0 :(得分:1)
有几件事:
b = 1
时,您不会考虑案例,例如100003 = 100003 ^ 1
,但您的函数返回[100003.0, 301.0]
。由于双精度损失,您的函数不会返回10000000000600000000009 = 100000000003 ^ 2
的有效结果。它将持续增加基数很长一段时间,直到它最终返回错误的结果。2 ^ 301
是一个90位数的数字。因此BigInteger
是唯一的选择。不幸的是,这将降低性能。x
,您也会继续内循环(增加指数)。如果x = 9
然后a = 2
和b = 4
我们已经16
,那么我们可以停止增加b
并增加a
代替(并重置{{} 1}}到1)。检查b
后,您的功能还会检查2 ^ 4
,2 ^ 5
和另外300个。以下是我提出的建议:
2 ^ 6
对于低于20亿的价值,它可能会更慢,但它能够处理任何规模的数字。
可以做的进一步优化:
private static final int MAX_B = 300;
public String[] power2(BigInteger x) {
increaseBase:
for (BigInteger a = BigInteger.ONE; a.compareTo(x) <= 0; a = a.add(BigInteger.ONE)) {
for (int b = 2; b <= MAX_B; b++) {
BigInteger result = a.pow(b);
if (result.equals(x)) {
return new String[] {String.valueOf(a), String.valueOf(b)};
}
if (result.compareTo(x) == 1) {
continue increaseBase;
}
}
}
return new String[] {String.valueOf(x), "1"};
}
而不是a
a < roundDown(sqrt(x))
a < x
另一个可能实际上最有效的想法是找到x
的素数因子。如果我们知道素数因子为x
,那么我们可以轻松计算出2, 2, 2, 2, 13, 13
和a = 2 * 2 * 13
。
修改强>
这是我的素因因素的实现。底部的测试和导入。
b = 2
指数的最大公约数是结果的最终指数。如果我们有数字public class PowerCalculator {
public String[] power2(String x) {
@SuppressWarnings("MismatchedQueryAndUpdateOfCollection") // update via increaseCountFor method
PrimeFactorToCount primeFactorToCount = new PrimeFactorToCount();
BigInteger number = new BigInteger(x);
BigInteger divisor = new BigInteger("2");
while (!number.equals(BigInteger.ONE)) {
if (number.remainder(divisor).equals(BigInteger.ZERO)) {
number = number.divide(divisor);
primeFactorToCount.increaseCountFor(divisor);
} else {
if (gcd(primeFactorToCount.values()) == 1) {
return new String[] {x, "1"};
}
divisor = divisor.add(BigInteger.ONE);
}
}
int gcd = gcd(primeFactorToCount.values());
return primeFactorToCount.entrySet().stream()
.map(entry -> new Exponentiation(entry.getKey(), entry.getValue()))
.map(exponentiation -> new Exponentiation(exponentiation.base.pow(exponentiation.exponent / gcd), gcd))
.reduce((a, b) -> new Exponentiation(a.base.multiply(b.base), gcd))
.map(Exponentiation::asStringArray)
.get();
}
private static int gcd(Collection<Integer> values) {
return values.stream()
.mapToLong(v -> v)
.mapToObj(BigInteger::valueOf)
.reduce(BigInteger::gcd)
.map(BigInteger::intValue)
.orElse(0);
}
private class Exponentiation {
private final BigInteger base;
private final int exponent;
private Exponentiation(BigInteger base, int exponent) {
this.base = base;
this.exponent = exponent;
}
private String[] asStringArray() {
return new String[] {String.valueOf(base), String.valueOf(exponent)};
}
}
private class PrimeFactorToCount extends HashMap<BigInteger, Integer> {
private void increaseCountFor(BigInteger primeFactor) {
if (containsKey(primeFactor)) {
put(primeFactor, get(primeFactor) + 1);
} else {
put(primeFactor, 1);
}
}
}
}
@RunWith(Parameterized.class)
public class PowerCalculatorTest {
private final uk.co.jpawlak.maptoobjectconverter.PowerCalculator powerCalculator = new uk.co.jpawlak.maptoobjectconverter.PowerCalculator();
@Parameterized.Parameters(name = "{index}: returns {1} for {0}")
public static Collection data() {
return asList(new Object[][] {
{input(3), expected(3, 1)},
{input(7 * 7), expected(7, 2)},
{input(2 * 2 * 2), expected(2, 3)},
{input(3 * 3 * 5 * 5), expected(3 * 5, 2)},
{input(3 * 3 * 5 * 5 * 5 * 5), expected(3 * 5 * 5, 2)},
{input(3 * 3 * 3 * 3 * 3 * 3 * 5 * 5), expected(3 * 3 * 3 * 5, 2)},
{input(2 * 3 * 3), expected(2 * 3 * 3, 1)},
});
}
private final String input;
private final String[] expected;
public PowerCalculatorTest(String input, List<String> expected) {
this.input = input;
this.expected = expected.stream().toArray(String[]::new);
}
@Test
public void test() {
String[] actual = powerCalculator.power2(input);
assertThat(actual, sameBeanAs(expected));
}
private static String input(long input) {
return String.valueOf(input);
}
private static List<String> expected(long base, long exponent) {
return ImmutableList.of(String.valueOf(base), String.valueOf(exponent));
}
}
import com.google.common.collect.ImmutableList;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import java.math.BigInteger;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import static com.shazam.shazamcrest.MatcherAssert.assertThat;
import static com.shazam.shazamcrest.matcher.Matchers.sameBeanAs;
import static java.util.Arrays.asList;
,则最终指数只能是108 = 2 ^ 2 * 3 ^ 3
。这就是为什么当我们要检查下一个除数时在循环中检查这个 - 所以它“快速失败”。这也是在return语句中1
用作指数的原因。我们来看看编号5184:
gcd
6和4的最大公约数是2。
可以做的进一步优化是找到数字的主要因素。如果你在5184 = 2^6 * 3^4 = (2^3)^2 * (3^2)^2 = 8^2 * 9^2 = (8*9)^2 = 72^2
等大素数的平方上尝试过它,那么在找到第一个素数因子之前,循环必须进行超过10亿次迭代。然而,这是完全不同的问题,所以我不会在这里描述它。
答案 1 :(得分:0)
试试这个
long[] power2(long x) {
double logX = Math.log(x);
for (int b = 300; b > 1; --b) {
double a = Math.exp(logX / b);
if (Math.abs(a - Math.round(a)) < 0.0005)
return new long[] { Math.round(a), b };
}
return new long[] { x, 1 };
}