乐趣区

关于后端:关于这个微信提现的问题太炸裂了以至于我写了段代码来验证

你好呀,我是歪歪。

周末的时候,我在网上看到一个对于微信钱包提现时,手续费收取的一个问题。

说真的,就这个问题吧,我集体感觉,放眼整个金融界,乃至于整个弱智吧,甚至于整个东半球,这都是一个相当炸裂的问题啊。

一时间,我竟然恍惚了起来:一眼看去,漏洞百出。然而仔细分析之后,竟然 TMD 无懈可击?!

哎呀,这个问题,你就不能细推敲,一推敲,脑瓜仁就疼。

你晓得的,我是一个口头派,所以我必定得先验证一波微信提现的手续费是否是这么多。

于是我发动提现了,发现的确是有至多一角钱的手续费:

另外,我发现安卓的手机,在这个页面中还无奈截图,所以我就只有通过拍照的形式搞到这个图片了,所以看起来有点顺当,你多担待一下小老弟。

那么第一个问题就来了:我提现一角,它手续费一角。请问我最终到手是多少钱呢?

0.1(元)-0.1(元)=0(元),提现的钱全副扣了手续费,所以没有钱到手。

对吗?

逻辑上是正当的,然而如果微信真的敢这样做的话,不就显得很可(傻)爱(逼)吗?

给你举个例子:假如,我找你借了 100 元钱,而后我通过微信还给你的银行卡(假如微信反对这个性能,相似于跨行转账),此时这笔转账对应的手续费是 1 元。如果从转账的金额外面扣除,你收到的钱是 99 元。

你感觉这正当吗?

如果你感觉正当的话,那么请借给我 1w 吧,我应个急,十分钟后就还你 9900 元。

什么,你问我还有 100 元呢?

别问,问就是手续费,你找微信要去。

所以,失常的逻辑是从你的余额外面扣除。比方在我余额还有 141.02 元的时候,我提取了一毛钱,那么我的余额变成了 140.82 元:

这里,我还暗藏了一个逻辑。

比方你只提现一分钱的时候,如果你的微信余额大于 0.1 元,那么也是要收取 0.1 元的手续费的。

这句话,听起来就想要流泪。

既然是从余额中扣除,那么当我余额只有一毛钱的时候,我再次提现一毛钱,这个时候余额不够扣了,会呈现什么状况呢?

我也连忙试了试:

140.82(元)-140.72(元)=0.1(元)

所以,我先给 Max 同学转给了 140.72(元):

此时我的微信钱包只剩下了 0.1 元:

这个时候,我再次提现 0.1 元的时候,发现微信竟然通知我:本次提现收费!!!

由此可得,当微信外面剩下的钱不够扣手续费的时候,本次提现就会收费。

这个收费,圈起来,前面要考。

试验做完了,先把钱要回来再说:

啊!

粗心了啊!

这样一来,我这篇文章的老本就很高了啊。我竟然一时间被试验冲昏了头脑,被动上交了私房钱?

然而我还有一个试验场景没有做啊?

就是当我微信钱包外面的钱大于 0.1
元钱的时候,我点击“全副提现”会呈现什么场景呢?

于是我以做试验的正当理由,胜利的要回了一分钱:

这样,我的余额就变成了 0.11 元:

于是,当我点击“全副提现”的时候,尽管我曾经预想到是这个场景了,然而我整个人还是缄默了,深深地缄默了。

我提现 0.11 元,手续费 0.1 元,到账 0.01 元。

也就是说,当你全副提现,且提现的金额大于手续费,即 0.1 元的时候,微信的逻辑是从你提取的钱外面扣除手续费。

也就是说我后面举得转账的例子,是真的有可能呈现转出去,钱少了的状况。

麻绳专挑细处断,厄运专找苦命人啊!

当初,我曾经失去论断了,所以我不能再输出明码了,再输出明码,又得痛失一毛钱!

试验当初曾经完结了,论断咱们也曾经有了。

那么,接下来,咱们再看看最开始的,那个让整个弱智吧,都为之“炸裂”的问题:

通过下面的试验,咱们得悉,这个问题中的这句话“如果我每次都只取 0.1,而后它手续费收 0.1”是没有任何问题的。

后半句:“就等于我一分钱都没有拿到”。

这句话是值得商讨的,因为通过试验证实,我最开始的时候,的确银行卡到账了 0.1 元。

然而,你要留神,我说“然而”了。

比方,我 1 元钱,每次提取 0.1 元,手续费 0.1 元,这样 5 次之后我微信外面的 1 元钱就变成了银行卡 0.5 元和微信收取的手续费 0.5 元。

那么,如果 …

我是说如果,我把我银行卡外面的 0.5 元再次充回到微信外面,持续反复下面的动作,事件是不是就开始变得乏味了?

所以,面试编程题就来了,请听题:

已知,在微信钱包提现任意金额,都会收取至多 0.1 元的手续费,然而当余额有余 0.1 元时除外。假如,小明当初有 100 元,他应该怎么操作,能力把这 100 元钱,尽可能的全副变成手续费,白白送给微信?

请给我一段 Java 代码,入参是微信钱包外面的余额,日志打印出对应的操作过程。

拿到题,先不慌,剖析一波。

首先,100 元,如果我每次只提取 0.1 元,收取 0.1 元手续费,那么当我操作 500 次之后,我还有 50 元。500 次,刚好是微信余额,100 元乘以 10,单位转化为角之后,再除以 2。

再把 50 元,存回去分 250 次取出来。250 次,刚好是微信余额,50 元乘以 10,单位转化为角之后,再除以 2。

再把 25 元存回去分 125 次 取出来。125 次,刚好是微信余额,25 元乘以 10,单位转化为角之后,再除以 2。

再把 12.5 块存回去分 62 次取出来,…

再把 6.2 存回去分 31 次取出来,…

周而复始,对吧。

也就说我每操作一次,我的微信余额会少 0.2 元。

联合后面举得例子,不难推理进去,我每一轮的操作次数,等于微信余额乘以 10,单位转化为角之后,再除以 2。

这个程序不难吧,起手就来:

public static void sbBehavior(double amount) {
    // 应该还有 amount 小于 0 的边界条件,节约篇幅,不写了。if (amount <= 0.1) {
        // 微信里的钱不够了扣手续费了, 操作完结
        System.out.println("麻花藤: 你只剩下:" + amount + "元了, 谢谢老铁~");
        return;
    }
    // 金额扩充十倍, 元转角, 好计算
    double totalJiao = amount * 10;
    // 每一轮的操作次数,等于微信余额除以 2
    int count = (int) (totalJiao / 2);
    // 每一轮完结之后, 共计手续费
    double fee = count * 0.1;
    // 每一轮完结之后, 银行卡里剩下的钱
    double remainder = count * 0.1;
    System.out.println("微信钱包原金额 =" + amount + "元, 操作次数 =" + count + "次, 手续费 =" + fee + "元, 残余金额 =" + remainder + "元");
    // 把银行卡里剩下的钱充回微信, 开始下一轮
    sbBehavior(remainder);
}

好,依照后面的思路,我写出了这个程序,你就先看这个程序有什么问题。我就明着通知你,这个程序必定是有问题的,你就去推敲,到底有哪些问题。

来,我问你:谁教你金额计算用浮点型的?回去等告诉吧。

当咱们的入参为 100 的时候,下面那个程序跑完之后,你会发现后果是这样的:

所以,牢记在心,只有波及到金额的计算,肯定肯定肯定要用 BigDecimal。而且我还附送你一条职场保命心经:用到 BigDecimal 时,具体保留多少小数位,具体的四舍五入规定,肯定肯定肯定要让需要提出方白纸黑字的写在需要外面,而不是你本人想当然的认为,保留两位小数,采纳四舍五入就行。前面出问题了,你啪的一下,就是把需要拿进去,你就不会很被动了。

回到咱们的程序中,所以咱们应该把程序修改成这样:

public static void sbBehavior(BigDecimal amount) {if (amount.compareTo(new BigDecimal(0.1)) <= 0) {
        // 微信里的钱不够了扣手续费了, 操作完结
        System.out.println("麻花藤: 你只剩下:" + amount + "元了, 谢谢老铁~");
        return;
    }
    // 金额扩充十倍, 元转角, 好计算
    BigDecimal jiao = amount.multiply(BigDecimal.TEN);
    // 每一轮的操作次数,等于微信余额除以 2
    BigDecimal count = jiao.divide(new BigDecimal(2), 0, RoundingMode.DOWN);
    // 每一轮完结之后, 共计手续费
    BigDecimal fee = count.multiply(new BigDecimal(0.1));
    // 每一轮完结之后, 银行卡里剩下的钱
    BigDecimal remainder = count.multiply(new BigDecimal(0.1));
    System.out.println("微信钱包原金额 =" + amount + "元, 操作次数 =" + count + "次, 手续费 =" + fee + "元, 残余金额 =" + remainder + "元");
    // 把银行卡里剩下的钱充回微信, 开始下一轮
    sbBehavior(remainder);
}

在下面的程序中,我把参加运行的中央全副都改成了 BigDecimal。然而这个程序还是有问题。

来,你持续去推敲,到底有哪些问题?

来,我问你:谁教你用 BigDecimal 参加计算的时候,用 new BigDecimal(0.1) 这个构造方法?

你用这个办法,idea 都会揭示你:老铁,听哥哥一句劝,还是用 String 类型的构造函数稳当一点。

就下面这个程序,我给你跑一下,你就发现问题了,同样还是有浮点数的问题:

所以,程序还得改一下,改成用 BigDecimal 的 String 类型的构造函数,其余啥都不动:

好,这个问题算是解决了。

你持续说,这个程序还有啥问题?

如果你没看进去的话,那么我带你看看输入后果:

在这一次输入的时候,手续费 6.2 元,残余金额 6.2 元,加起来才 12.4 元。然而我“微信钱包原金额”是 12.5 元啊?

还有一分钱去哪里了呢?

所以我在一开始分析题的时候就给你下了一个套:

100 元,操作 500 次之后,还有 50 元。50 元,操作 250 次之后,还有 25 元。25 元,操作 150 次之后,还有 12.5 元 …

如果你没有带着本人的思考看文章的话,那么你可能就默认为操作一次之后,手续费和银行卡的余额都会减少 0.1 元。

也就是手续费和银行卡的金额,和操作次数相干,所以写出了这样的代码:

手续费,的确是每操作一次之后扣除 0.1 元,的确是和操作次数正相干。然而残余的钱,应该是用以后这一轮残余的总金额减去以后这一轮的总手续费。

也就是要把这一行代码批改为这样:

拿着这个程序去跑的时候,你会发现输入失常了,每一轮的金额加起来都能相等了:

这下没有任何故障了。

那么,留神,我当初要开始变形了。我要把题目变成:

请给我一段 Java 代码,入参是微信钱包外面的余额,出参是一共须要操作多少次。

在题目中,加了总操作次数的出参,我曾经晓得了每一轮操作的次数,算总次数这还不是手到擒来的事件?

分分钟拿出代码:

跑出后果:

咱们能够看到是 999 次。是的,不要质疑这个后果,当你有 100 元钱的时候,只须要操作 999 次,你就把本人的 99.9 元都给到微信了。

诶,敌人,你留神看,当我把金额变成 50 元的时候,总次数就是 499 了:

当我把金额变成 9.9 元的时候,总次数就变成了 98 次:

所以,请留神,我要“所以”了。

所以,如果我只要求操作的总次数,不要求输入过程,那么代码应该是怎么样的?

是不是把金额扩充十倍,变成角票,而后减去本人留下的一角钱,就是操作的总次数:

public static int sbBehavior(BigDecimal amount) {return amount.multiply(BigDecimal.TEN).subtract(BigDecimal.ONE).intValue();}

这样不就完事了吗?

你遗记后面的所有内容,认真的想想,是不是的确是这个情理?

假如你有 100 元,无论你怎么操作,微信每次只会收 0.1 元的手续费,而你最多只会剩下 0.1 元。

那么你必定得至多操作 999 次啊,这个小弯儿能转过来吧?

好,转过来了,是吧?

我再问你一个问题,假如我只有 0.19 元,我要把钱给微信,最多操作一次,而后我给它 0.1 元对吧?

然而,你用下面我给你的代码跑进去只会,输入是 0:

是的,这个代码还是有问题的。

我就明确的通知你,这个代码只实用于金额在 100.9 元到 0.19 元之间的数字。

至于为什么,本人去推敲。然而我不倡议你去推敲,因为这个玩意整个从最开始的中央就走偏了。

当初,请你遗记后面所有的代码,因为后面的代码,全都是错的,我全程都在误导你,让你顺着我的思路走。

其实你回忆一下,最开始的时候,我为什么要假如你微信外面只有 100 元钱?

因为 100 元钱对应的手续费,不管你是提取一角钱,还是提取 100 元,100*0.001=0.1 元,都刚好是 0.1 元。

而后我就开始通知你,每次提现到银行卡 0.1 元,手续费 0.1 元,巴拉巴拉巴拉~

然而,你有没有想过,或者是看到哪个局部的时候,才豁然开朗:如果我有 1000 元呢?

如果我有 1000 元,那么我第一次全副提现的话,手续费就是 1 元啊,而不是 0.1 元啊?

所以,你当初回过头去看这行代码,是不是特地的搞笑:

怎么会去先计算次数,再依据次数反算其金额呢?

为了尽快的把钱都给到微信,必定是每次尽量给到更多的手续费。已知手续费率是固定的,那么提现的金额越高,手续费越高对吧?

所以,正确的操作应该是每次把微信钱包外面的钱全副都取出来,也就是基于微信钱包外面的钱,去计算手续费,计算残余的钱。

转变了外围思路之后,代码就变成了这样:

我也给你放一个粘过来就能用的代码:

public static int sbBehavior(BigDecimal amount, int totalTimes) {if (amount.compareTo(new BigDecimal("0.1")) <= 0) {
        // 微信里的钱不够了扣手续费了, 操作完结
        System.out.println("麻花藤: 你只剩下:" + amount + "元了, 谢谢老铁~");
        return totalTimes;
    }
    // 基于微信钱包外面的钱,去计算手续费
    BigDecimal fee = amount.multiply(new BigDecimal("0.001")).setScale(2, BigDecimal.ROUND_UP);
    // 手续费有余 0.1 元, 则补齐为 0.1 元
    if (fee.compareTo(new BigDecimal("0.1")) <= 0) {fee = new BigDecimal("0.1");
    }
    // 提现到银行卡的钱
    BigDecimal remainder = amount.subtract(fee);
    totalTimes++;
    System.out.println("原现金 =" + amount + "元, 操作 =" + totalTimes + "次后, 手续费 =" + fee + "元, 还剩下 =" + remainder + "元");
    // 把银行卡里剩下的钱充回微信, 开始下一轮
    return sbBehavior(remainder, totalTimes);
}

这样,当咱们有 1000 元的时候,每次做“全副提现”的动作,只须要操作 3257 次:

如果咱们还是用之前一毛钱一毛钱的提法,得搞 9999 次。

效率晋升 300%+。

难受了!

而且这个是通用的逻辑,你就算给它 100 元,它也能给你算出是 999 次:

给它 0.19 元,它能给你算出是 1 次:

没有任何故障,然而,不晓得你看到这里,是否产生了一个疑难:为什么咱们要把钱尽可能的给微信呢?

那我给你换个角度:咱们应该怎么操作,才应该防止给微信手续费呢?

这样一想,是不是思路就关上了?

最初,如果这篇文章有那么一个霎时,让你笑了一下的话,那么,求个收费的“赞”,不过分吧?

退出移动版