Swift中是否有一种方法可以计算所有可能的方法,即可以对3个数字进行加,乘,减或除运算包括括号

问题描述

我正在用Xcode开发游戏,需要弄清楚3个数字的所有可能的数学结果。玩家试图使用这三个数字来尽可能接近目标数字。例如,如果目标为10,而您的三个数字分别为8、6和4,则可以使用8 + 6-4。如果目标数字为12,则可以使用8 * 6/4并得到12。运行142种可能的组合并将结果存储在数组中

resultsArray[0] = firstNum+secondNum+thirdNum; 
resultsArray[1] = firstNum+secondNum-thirdNum; 
resultsArray[2] = firstNum+secondNum*thirdNum; 
resultsArray[3] = firstNum+secondNum/thirdNum; 
...
resultsArray[143] = thirdNum/(secondNum-firstNum);

然后我正在检查数组中最接近的正确答案,如下所示:

let x = 10 //target number
let closest = resultsArray.enumerated().min( by: { abs($0.1 - x) < abs($1.1 - x) } )!
correctAnswer = resultsArray[closest.offset] 

除除零错误外,它都起作用。

我知道有一个更好的方法,但是我已经在网上搜寻并空手而归。 有什么想法吗?

解决方法

这是一个有趣的问题,让您使用Swift的自定义枚举,可选,集,高阶函数(map)的功能以及从一个函数返回多个值的功能。

144个方程!即使我们有代码,也很难确认您已涵盖所有内容。除以零是一个棘手的情况,需要特别注意。

这是我对这个问题的看法。我的解决方案的目标是:

  1. 将其分解为易于验证的步骤
  2. 避免被零除
  3. 避免分数计算
  4. 避免使用负数
  5. 以易于阅读的形式显示方程式
  6. 找到最接近目标的所有方程式
// Generate all 6 permuations of the 3 numbers.
// Use Set() to remove duplicates
func permuteNumbers(_ a: Int,_ b: Int,_ c: Int) -> [(Int,Int,Int)] {
    return Set([[a,b,c],[a,c,b],[b,a,a],[c,a]]).map { ($0[0],$0[1],$0[2]) }
}

enum Operation: String,CaseIterable {
    case addition = "+"
    case subtraction = "-"
    case multiplication = "*"
    case division = "/"
}

// Generate all 16 combinations of the 4 operations
func allOperations() -> [(Operation,Operation)] {
    var all = [(Operation,Operation)]()
    for op1 in Operation.allCases {
        for op2 in Operation.allCases {
            all.append((op1,op2))
        }
    }
    return all
}

// Return nil on divide by zero.
// Return nil when the result would be a negative number.
// Return nil when the result would be a fraction (not a whole number).
func performOperation(_ a: Int,_ op: Operation) -> Int? {
    switch op {
    case .addition:        return a + b
    case .subtraction:     return (b > a) ? nil : a - b
    case .multiplication:  return a * b
    case .division:        return ((b == 0) || (a % b != 0)) ? nil : a / b
    }
}

// Perform (a op1 b) op2 c
// return (result,equation)
func performOp1First(a: Int,b: Int,c: Int,op1: Operation,op2: Operation) -> (Int?,String) {
    let str = "(\(a) \(op1.rawValue) \(b)) \(op2.rawValue) \(c)"
    
    if let r1 = performOperation(a,op1) {
        if let r2 = performOperation(r1,op2) {
            return (r2,str)
        }
    }
    return (nil,str)
}

// Perform a op1 (b op2 c)
// return (result,equation)
func performOp2First(a: Int,String) {
    let str = "\(a) \(op1.rawValue) (\(b) \(op2.rawValue) \(c))"

    if let r1 = performOperation(b,op2) {
        if let r2 = performOperation(a,r1,op1) {
            return (r2,str)
}

// Perform a op1 b op2 c - order doesn't matter for (+,+),(+,-),(*,*),and (*,/)
// return (result,equation)
func performNoParens(a: Int,String) {
    let str = "\(a) \(op1.rawValue) \(b) \(op2.rawValue) \(c)"
    
    if let r1 = performOperation(a,str)
}

// Search all permutations of the numbers,operations,and operation order
func findBest(a: Int,target: Int) -> (diff: Int,equations: [String]) {
    let numbers = permuteNumbers(a,c)
    
    var best = Int.max
    var equations = [String]()
    
    for (a,c) in numbers {
        for (op1,op2) in allOperations() {
            // Parentheses are not needed if the operators are (+,/)
            let noparens = [["+","+"],["+","-"],["*","*"],"/"]].contains([op1.rawValue,op2.rawValue])
            
            for f in (noparens ? [performNoParens] : [performOp1First,performOp2First]) {
                let (result,equation) = f(a,op1,op2)
                if let result = result {
                    let diff = abs(result - target)
                    if diff == best {
                        equations.append(equation)
                    } else if diff < best {
                        best = diff
                        equations = [equation]
                    }
                }
            }
        }
    }
    
    return (best,equations)
}

示例:

print(findBest(a: 8,b: 6,c: 4,target: 10))
(diff: 0,equations: ["8 + 6 - 4","6 + 8 - 4","(6 - 4) + 8","(8 - 4) + 6"])
print(findBest(a: 8,target: 12))
(diff: 0,equations: ["6 * 8 / 4","8 * 6 / 4","(8 / 4) * 6"])
print(findBest(a: 8,target: 4))
(diff: 0,equations: ["6 - (8 / 4)","8 / (6 - 4)"])
print(findBest(a: 8,target: 5))
(diff: 1,"4 + 8 - 6","(8 - 6) + 4","8 - (6 - 4)","8 / (6 - 4)","8 + 4 - 6"])
print(findBest(a: 8,target: 7))
(diff: 1,equations: ["(8 - 6) + 4","(8 - 6) * 4","6 + (8 / 4)","4 * (8 - 6)","8 + 4 - 6","(8 / 4) + 6"])

科林版本

这里是从Swift版本手动翻译过来的Kotlin版本。这是我的第一个Kotlin程序,所以我确定我并不是以最惯用的方式进行任何操作。我在Online Kotlin Playground

上测试了该程序
import kotlin.math.abs

// Generate all 6 permuations of the 3 numbers.
// Use Set() to remove duplicates
fun permuteNumbers(a: Int,c: Int): Set<List<Int>> {
    return setOf(
        listOf(a,c),listOf(a,b),listOf(b,a),listOf(c,a)
    )
}

enum class Operation(val string: String) { 
  ADDITION("+"),SUBTRACTION("-"),MULTIPLICATION("*"),DIVISION("/")
}

fun allOperations(): List<Pair<Operation,Operation>> {
    val result = mutableListOf<Pair<Operation,Operation>>()
    for (op1 in Operation.values()) {
        for (op2 in Operation.values()) {
            result.add(Pair(op1,op2))
        }
    }
    
    return result
}

fun performOperation(a: Int,op: Operation): Int? {
    return when (op) {
        Operation.ADDITION       ->  (a + b)
        Operation.SUBTRACTION    ->  if (b > a) { null } else { a - b }
        Operation.MULTIPLICATION ->  a * b
        Operation.DIVISION       ->  if ((b == 0) || (a % b != 0)) { null} else { a / b }
    }
}

// Perform (a op1 b) op2 c
// return (result,equation)
fun performOp1First(a: Int,op2: Operation): Pair<Int?,String> {
    val str = "($a ${op1.string} $b) ${op2.string} $c"
    
    performOperation(a,op1)?.also { r1 ->
        performOperation(r1,op2)?.also { r2 ->
            return Pair(r2,str)
        }
    }
    return Pair(null,equation)
fun performOp2First(a: Int,String> {
    val str = "$a ${op1.string} ($b ${op2.string} $c)"
    
    performOperation(b,op2)?.also { r1 ->
        performOperation(a,op1)?.also { r2 ->
            return Pair(r2,equation)
fun performNoParens(a: Int,String> {
    val str = "$a ${op1.string} $b ${op2.string} $c"
    
    performOperation(a,and operation order
fun findBest(a: Int,target: Int): Pair<Int,List<String>> {
    val numbers = permuteNumbers(a,c)
    
    var best = Int.MAX_VALUE
    var equations = mutableListOf<String>()
    
    for ((a1,b1,c1) in numbers) {
        for ((op1,op2) in allOperations()) {
            // Parentheses are not needed if the operators are (+,/)
            val noparens = listOf(listOf("+","+"),listOf("+","-"),listOf("*","*"),"/"))
                .contains(listOf(op1.string,op2.string))
            
            for (f in if (noparens) { listOf(::performNoParens) } else { listOf(::performOp1First,::performOp2First) }) {
                val (result,equation) = f(a1,c1,op2)
                result?.also { result2 ->
                    val delta = abs(target - result2)
                    if (delta == best) {
                        equations.add(equation)
                    } else if (delta < best) {
                        best = delta
                        equations = mutableListOf(equation)
                    }
                }
            }
            
        }
    }
    
    return Pair(best,equations)
}

fun main() {
    println(findBest(4,6,8,4))
}