寻找幸运数

问题描述

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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值