A recent discussion about WeChat wallet withdrawal fees sparked an interesting algorithmic problem. The question: given a 0.1 yuan minimum fee per withdrawal (waived if balance is insufficient), how many operations are needed to transfer all money to WeChat as fees?
Initial Experiments
First, I verified the fee structure. Withdrawing 0.1 yuan incurs a 0.1 yuan fee. When balance equals exactly 0.1 yuan, the withdrawal becomes free (insufficient fee deduction).
For partial withdrawals (e.g., 0.11 yuan), the fee is deducted from the withdrawn amount: you receive 0.01 yuan. For "withdraw all" when balance exceeds 0.1 yuan, the fee is also taken from the withdrawn sum.
First Attempt: Flawed Logic
My initial reasoning: if I repeatedly withdraw 0.1 yuan, each operation reduces balance by 0.2 yuan (0.1 withdrawn + 0.1 fee). With 100 yuan, that's 500 operations, leaving 50 yuan. Repeat: 250 ops → 25 yuan, 125 ops → 12.5 yuan, etc.
Initial Java code (flawed):
public static void sbBehavior(double amount) {
if (amount <= 0.1) {
System.out.println("Remaining: " + amount);
return;
}
double totalJiao = amount * 10;
int count = (int) (totalJiao / 2);
double fee = count * 0.1;
double remainder = count * 0.1;
System.out.println("Count=" + count + ", fee=" + fee + ", remainder=" + remainder);
sbBehavior(remainder);
}
Problems:
- Using
doublefor monetary calculations – precision issues. new BigDecimal(0.1)introduces floating-point errors.- Remainder calculation was wrong: remainder = initial - fee, not count * 0.1.
Corrected BigDecimal Version
Proper implementation using BigDecimal with string constructor:
public static void sbBehavior(BigDecimal amount) {
if (amount.compareTo(new BigDecimal("0.1")) <= 0) {
System.out.println("Remaining: " + amount);
return;
}
BigDecimal jiao = amount.multiply(BigDecimal.TEN);
BigDecimal count = jiao.divide(new BigDecimal("2"), 0, RoundingMode.DOWN);
BigDecimal fee = count.multiply(new BigDecimal("0.1"));
BigDecimal remainder = amount.subtract(fee);
System.out.println("Count=" + count + ", fee=" + fee + ", remainder=" + remainder);
sbBehavior(remainder);
}
Key Insight: The Assumption Was Wrong
The whole premise of withdrawing 0.1 yuan repeatedly is suboptimal! For larger balances, the fee = 0.1% of withdrawal amount (minimum 0.1 yuan). With 1000 yuan, a single full withdrawal costs 1 yuan fee, not 0.1.
Optimal strategy: Withdraw the entire balance each time to maximize fees per operation.
Final Efficient Implementation
public static int optimizeFees(BigDecimal amount, int totalTimes) {
if (amount.compareTo(new BigDecimal("0.1")) <= 0) {
System.out.println("Remaining: " + amount);
return totalTimes;
}
BigDecimal fee = amount.multiply(new BigDecimal("0.001"))
.setScale(2, RoundingMode.UP);
if (fee.compareTo(new BigDecimal("0.1")) < 0) {
fee = new BigDecimal("0.1");
}
BigDecimal remainder = amount.subtract(fee);
totalTimes++;
System.out.println("Amount=" + amount + ", op=" + totalTimes +
", fee=" + fee + ", remainder=" + remainder);
return optimizeFees(remainder, totalTimes);
}
Results Comparison
| Initial Amount | 0.1-yuan strategy ops | Full-withdrawal strategy ops |
|---|---|---|
| 100 yuan | 999 | 999 (same, due to fee floor) |
| 1000 yuan | 9999 | 3257 |
For amounts > 100 yuan, the full-withdrawal approach is 3x+ more efficient at transferring money to fees.
The Simpler Math
If you don't need the process, just total operations:
- For amounts where fee rate applies linearly:
totalOps = (amount * 10) - 1? - But this fails for small amounts like 0.19 yuan (should be 1, but formula gives 0).
The correct general formula depends on the fee structure and rounding behavior, which is why the iterative approach is safer.
Final Thought
Instead of maximizing fees, consider the inverse: how to minimize fees? Perhaps by only withdrawing when balance exceeds 0.1 yuan and using the free withdrawal when balance is low.