问题描述
4和7是幸运数字,由它们所组成的数也是幸运数字。例如:44,47,474,7447等等。这一系列幸运数由小到大构成了幸运数列。现在,需要设计一个算法找出第N个幸运数是多少。
问题分析
我们可以将幸运数列分组:
(4,7)(44,47,74,77)(444,447,474,477,744,747,774,777)……
可以看到一位数的幸运数有2个,两位数的幸运数有4个,三位数的幸运数有8个……n位数的幸运数有
2n
个。因此,我们可以先通过循环累加确认第N个幸运数是几位数。
var i = 2,
sum = 2,
k = 1;
while (sum <= n) {
k++;
i *= 2;
sum += i;
}
console.log(k)
确定第N个数为k位数之后,我们就可以方便地确定其为第几个k位数。
var temp = n - (sum - i);//第temp个k位数
最后,最重要的一步,我们发现:这个幸运数的形式和二进制类似。通过进一步思考可以发现,将temp转为二进制似乎可以解决这个问题,但是需要进行调整,即使用temp-1。
对于N=20,计算后可得,temp=6,temp-1转换为二进制后为101,只包含三位数字,因此需要使用0在其高位进行补齐。
算法实现
function findLucky1(n) {
if (n == 1) return 4;
if (n == 2) return 7;
var i = 2,
sum = 2,
k = 1;
while (sum <= n) {
k++;
i *= 2;
sum += i;
}
var temp = n - (sum - i) - 1;
var str = temp.toString(2);
var len = str.length;
for (var j = 0; j < (k - len); j++) {
str = '0' + str;
}
str = str.replace(/0/g, '4');
str = str.replace(/1/g, '7');
return str;
}
console.log(findLucky1(20));//4747
更简便的实现
显然,使用while循环确定位数不是一个最优的方式,可以通过简单的log求对数的方式来进行确定。
修改后的代码如下
function findLucky2(n) {
if (n == 1) return 4;
if (n == 2) return 7;
var index = Math.floor(Math.log(n + 1) / Math.log(2)),
temp = n - (Math.pow(2, index) - 2) - 1,
str = temp.toString(2),
len = str.length;
for (var j = 0; j < (index - len); j++) {
str = '0' + str;
}
str = str.replace(/0/g, '4');
str = str.replace(/1/g, '7');
return str;
}
console.log(findLucky2(20));//4747
使用位运算
上一个方法中,使用了for循环进行二进制的高位补齐。那么还有没有什么更简便的方法来补齐高位呢?显然是有的。可以使用位运算&来实现,实现方式如下
function findLucky3(n) {
if (n == 1) return 4;
if (n == 2) return 7;
var index = Math.floor(Math.log(n + 1) / Math.log(2)),
subIndex = n - (Math.pow(2, index) - 2) - 1,
temp = (1 << index) & subIndex,//使用位运算
str = temp.toString(2);
str = str.replace(/0/g, '4');
str = str.replace(/1/g, '7');
return str.substring(1, str.length);
}
console.log(findLucky3(20));//4747