从“提现手续费”,我想到一个面试题,你来试一试?

2023-11-11 22:40

本文主要是介绍从“提现手续费”,我想到一个面试题,你来试一试?,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

你好呀,我是歪歪。

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

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

一时间,我居然恍惚了起来:一眼看去,漏洞百出。但是仔细分析之后,居然 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;//每一轮的操作次数,等于微信余额除以 2int 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);//每一轮的操作次数,等于微信余额除以 2BigDecimal 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 次:

没有任何毛病,但是,不知道你看到这里,是否产生了一个疑问:为什么我们要把钱尽可能的给微信呢?

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

这样一想,是不是思路就打开了?

这篇关于从“提现手续费”,我想到一个面试题,你来试一试?的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/393308

相关文章

Java面试题:通过实例说明内连接、左外连接和右外连接的区别

在 SQL 中,连接(JOIN)用于在多个表之间组合行。最常用的连接类型是内连接(INNER JOIN)、左外连接(LEFT OUTER JOIN)和右外连接(RIGHT OUTER JOIN)。它们的主要区别在于它们如何处理表之间的匹配和不匹配行。下面是每种连接的详细说明和示例。 表示例 假设有两个表:Customers 和 Orders。 Customers CustomerIDCus

【SparkStreaming】面试题

Spark Streaming 是 Apache Spark 提供的一个扩展模块,用于处理实时数据流。它使得可以使用 Spark 强大的批处理能力来处理连续的实时数据流。Spark Streaming 提供了高级别的抽象,如 DStream(Discretized Stream),它代表了连续的数据流,并且可以通过应用在其上的高阶操作来进行处理,类似于对静态数据集的操作(如 map、reduce、

Java线程面试题(50)

不管你是新程序员还是老手,你一定在面试中遇到过有关线程的问题。Java语言一个重要的特点就是内置了对并发的支持,让Java大受企业和程序员的欢迎。大多数待遇丰厚的Java开发职位都要求开发者精通多线程技术并且有丰富的Java程序开发、调试、优化经验,所以线程相关的问题在面试中经常会被提到。 在典型的Java面试中, 面试官会从线程的基本概念问起, 如:为什么你需要使用线程,

C语言常见面试题3 之 基础知识

(1)i++和++i哪个效率更高? 对于内建数据类型,二者效率差别不大(去除编译器优化的影响) 对于自定义数据类型(主要是类),因为前缀式(++i)可以返回对象的引用;而后缀式(i++)必须返回对象的值,所以导致在大对象时产生了较大的复制开销,引起效率降低。 (2)不使用任何中间变量如何交换a b的值? void swap(int& a, int& b)//采用引用传参的方式{a^=

Java面试题:内存管理、类加载机制、对象生命周期及性能优化

1. 说一下 JVM 的主要组成部分及其作用? JVM包含两个子系统和两个组件:Class loader(类装载)、Execution engine(执行引擎)、Runtime data area(运行时数据区)、Native Interface(本地接口)。 Class loader(类装载):根据给定的全限定名类名(如:java.lang.Object)装载class文件到Runtim

面试题3:GET 和 POST 有什么区别?

[!]高频面试题。 GET 和 POST 没有本质区别,可以进行相互代替。 1、GET语义:“从服务器获取数据”;POST语义:“往服务器上提交数据”。[设计初衷,不一定要遵守] 2、发请求时,给服务器传递的数据,GET 一般是放在查询字符串中,但GET 也可以把数据放在 body 里。不过比较少见,以至于浏览器不一定能支持,不过其他的http客户端可以支持;POST 一般是放在 body 中

2025秋招NLP算法面试真题(二)-史上最全Transformer面试题:灵魂20问帮你彻底搞定Transformer

简单介绍 之前的20个问题的文章在这里: https://zhuanlan.zhihu.com/p/148656446 其实这20个问题不是让大家背答案,而是为了帮助大家梳理 transformer的相关知识点,所以你注意看会发现我的问题也是有某种顺序的。 本文涉及到的代码可以在这里找到: https://github.com/DA-southampton/NLP_ability 问题

神魔?居然还有人认为这些初中级VUE面试题简单!!?

前言 ❝ 上次写了一篇 不会吧!都2020年了你还不会这些VUE面试题? 我遇到的一面的面试题,有小伙伴觉得这些题太easy了   ❞ 我反思!! 今天总结了一些比较新的VUE面试题,适用于初中级的面试者,也是我在面试中遇到概率比较高的题目 「面对疾风吧!!」 1.keep-alive的作用是什么? <keep-alive>是Vue的内置组件,能够缓存组件,防止重复渲染

前端面试题(基础篇七)

一、谈谈你对webpack的看法 webpack是一个模块打包工具,我们可以使用webpack管理我们的模块依赖,编译输出模块所需的静态文件。它可以很好的管理、打包web开发中所需的html、css、JavaScript以及其他各种静态文件(使用的图片、字体图标等),让开发变得更加高效。对于不同类型的资源,webpack都有对应的模块加载器,webpack 模块打包器会分析模块间的 依赖关系,最

华为面试题及答案——机器学习(一)

(1). 线性回归普通最小二乘法运用的经典基本假设有哪些? 线性回归中,普通最小二乘法(Ordinary Least Squares, OLS)是一种常用的估计方法。 线性关系假设: 假设自变量(X)与因变量(Y)之间存在线性关系。即,模型可以表示为 Y=β0+β1X1+β2X2+...+βnXn+ϵY = \beta_0 + \beta_1X_1 + \beta_2X_2 + ... +