Good morning! Here’s your coding interview problem for today.
This problem was asked by Microsoft.
A number is considered perfect if its digits sum up to exactly 10.
Given a positive integer n, return the n-th perfect number.
For example, given 1, you should return 19. Given 2, you should return 28.
题目意思:所谓perfect number ,指一个正整数,如果这个数的每一位数字加起来等于10。第一个perfect number是19 (1+9=10),第二个是28(2+8=10)。要求你写个函数能够返回第n个perfect number。
solution:
方法一:暴力查找
直接从start=19开始计算,每次判断下是否为完美数字,如果是,则count++ ,然后start+1,进行下次判断。
/**
* 暴力查找的方式
*
* @param nth
* @return
*/
public static List<Integer> findNthMethod1List(int nth) {
List<Integer> list = new ArrayList<>(nth);
int start = 19;
int count = 0;
while (count <= nth) {
int temp = start;
int sum = 0;
while (temp != 0) {
sum = temp % 10 + sum;
temp = temp / 10;
}
if (sum == 10) {
list.add(start);
count++;
}
start++;
}
return list;
}
方法二:增加步长,提高效率
方法一中start++的方式,比较慢。我们可以每次start= start+9 ,这样会快很多。如下:
public static int findNthMethod2(int n) {
int count = 0;
for (int curr = 19; ; curr += 9) {
// Find sum of digits in current no.
int sum = 0;
for (int x = curr; x > 0; x = x / 10) {
sum = sum + x % 10;
}
// If sum is 10, we increment count
if (sum == 10) {
count++;
}
// If count becomes n, we return current
// number.
if (count == n) {
return curr;
}
}
}
注意:
1.这里虽然每次可以+9,但是有个问题,比如假设到271, 280, 下一个完美数就并不是289。网上不少人写的算法有这个问题,认为可以直接通过n而不需要count变量来得到第n个完美数。这就是问题的关键点,我们找不到一个公式将完美数与n直接关联上,必须通过count来计数,从count=0一直累加到count= n。否则,我们只能保证算法返回的是完美数,但是这个数很可能并不是第n个。原文的方法三就是个错误的算法,我在最后面的测试代码中有对比。
2.步长不仅可以是9,其实也可以是10。从0开始,每次加10,此时start=10 ,start/10=1, 此时每个位数字和位1,则末尾就是10-1=9,第一个完美数就是19, start=19+10 , 29/10=2 ,则末尾就是10-2=8 ,第二个完美数就是28。如果上一个完美数是280,则(280+10)/10=29 , 2+9>10 , 所以继续加10:290+10=300 ,300/10=30 ,则末尾就是10-3-0=7 ,这个完美数就是307 ,依次类推。这里9比10好,就是因为加9刚好让低位减少1,高位+1,再大多数情况下,+9都能直接得到完美数,不用再推算最后一位的值。
下面是完整的代码,增加了对数器来用生成测试用例,检测算法是否正确:
package testAlgorithms;
import java.util.ArrayList;
import java.util.List;
public class FindPerfectNumberTest {
public static void main(String[] args) {
System.out.println(isPerfect(18));
System.out.println(isPerfect(28));
System.out.println("checkAlgorithm:" + checkAlgorithm(10000));
}
/**
* 检测算法是否正确
*
* @param nth
* @return
*/
public static boolean checkAlgorithm(int nth) {
long startTime = System.currentTimeMillis();
// List<Integer> myList = findNthMethod2List(nth);
// List<Integer> myList = findNthMethod3List(nth);
List<Integer> myList = findNthErrMethodList(nth);
long endTime = System.currentTimeMillis();
System.out.println(" effective method use time :" + ( endTime-startTime));
startTime = System.currentTimeMillis();
List<Integer> correctList = findNthMethod1List(nth);
// List<Integer> correctList = findNthMethod3List(nth);
endTime = System.currentTimeMillis();
System.out.println(" bruce method use time :" + (endTime-startTime));
for (int i = 0; i < myList.size(); i++) {
if (!myList.get(i).equals(correctList.get(i))) {
System.out.println("myList: " + myList);
System.out.println("correctList: " + correctList);
System.out.println("wrong nth:" + (i + 1) + " ; my: " + myList.get(i) + " ; correct : " + correctList.get(i));
return false;
}
}
return true;
}
/**
* 暴力查找的方式
*
* @param nth
* @return
*/
public static int findNthMethod1(int nth) {
int start = 19;
int count = 0;
while (count <= nth) {
int temp = start;
int sum = 0;
while (temp != 0) {
sum = temp % 10 + sum;
temp = temp / 10;
}
if (sum == 10) {
count++;
return start;
}
start++;
}
return start;
}
/**
* 暴力查找的方式
*
* @param nth
* @return
*/
public static List<Integer> findNthMethod1List(int nth) {
List<Integer> list = new ArrayList<>(nth);
int start = 19;
int count = 0;
while (count <= nth) {
int temp = start;
int sum = 0;
while (temp != 0) {
sum = temp % 10 + sum;
temp = temp / 10;
}
if (sum == 10) {
list.add(start);
count++;
}
start++;
}
return list;
}
public static boolean isPerfect(int value) {
int sum = 0;
while (value != 0) {
sum = value % 10 + sum;
value = value / 10;
}
if (sum == 10) {
return true;
}
return false;
}
/**
* 步长为9
* @param n
* @return
*/
public static int findNthMethod2(int n) {
int count = 0;
for (int curr = 19; ; curr += 9) {
// Find sum of digits in current no.
int sum = 0;
for (int x = curr; x > 0; x = x / 10) {
sum = sum + x % 10;
}
// If sum is 10, we increment count
if (sum == 10) {
count++;
}
// If count becomes n, we return current
// number.
if (count == n) {
return curr;
}
}
}
public static List<Integer> findNthMethod2List(int n) {
List<Integer> list = new ArrayList<>(n);
int count = 0;
for (int curr = 19; ; curr += 9) {
// Find sum of digits in current no.
int sum = 0;
for (int x = curr; x > 0; x = x / 10) {
sum = sum + x % 10;
}
// If sum is 10, we increment count
if (sum == 10) {
list.add(curr);
count++;
}
// If count becomes n, we return current
// number.
if (count == n) {
return list;
}
}
}
/**
* 步长为10的
* @param n
* @return
*/
public static List<Integer> findNthMethod3List(int n) {
List<Integer> list = new ArrayList<>(n);
int count = 0;
for (int curr = 0; ; curr += 10) {
// Find sum of digits in current no.
int sum = 0;
int temp = curr/10;
for (int x = curr/10; x > 0; x = x / 10) {
sum = sum + x % 10;
}
if(sum > 10 ||sum == 0){
continue;
}else {
int left = 10 - sum;
list.add(temp*10+left);
count++;
}
// If count becomes n, we return current
// number.
if (count == n) {
return list;
}
}
}
/**
* Error Method ,wrong algorithm
* @param n
* @return
*/
public static int findNthErrMethod(int n)
{
int nthElement = 19 + (n - 1) * 9;
int outliersCount = (int)Math.log10(nthElement) - 1;
// find the nth perfect number
nthElement += 9 * outliersCount;
return nthElement;
}
/**
* Error Method ,wrong algorithm
* @param n
* @return
*/
public static List<Integer> findNthErrMethodList(int n)
{
List<Integer> list = new ArrayList<>(n);
for (int i = 1; i <= n ;i++){
list.add(findNthErrMethod(i));
}
return list;
}
}