如何获得Swift语言中某些整数的功效?


102

我最近学的很快,但是我有一个基本问题,找不到答案

我想得到像

var a:Int = 3
var b:Int = 3 
println( pow(a,b) ) // 27

但是pow函数只能使用双数,不能使用整数,而且我什至无法将int转换为Double(a)或a.double()之类的东西。

为什么它不提供整数的幂?它一定会毫无疑问地返回一个整数!为什么不能将整数转换为双精度?只需将3更改为3.0(或3.00000 ...即可)

如果我有两个整数,并且想进行幂运算,该如何平稳地执行呢?

谢谢!


这些类型声明是错误的
Edgar Aroutiounian 2014年


1
@phuclv的注释指出了对该主题的精彩讨论。我会将链接中的文本更改为“这些原因”
eharo2

Answers:


78

如果愿意,可以声明一个infix operator

// Put this at file level anywhere in your project
infix operator ^^ { associativity left precedence 160 }
func ^^ (radix: Int, power: Int) -> Int {
    return Int(pow(Double(radix), Double(power)))
}

// ...
// Then you can do this...
let i = 2 ^^ 3
// ... or
println("2³ = \(2 ^^ 3)") // Prints 2³ = 8

我使用了两个插入符号,因此仍然可以使用XOR运算符

Swift 3更新

在Swift 3中,“魔术数字” precedence被替换为precedencegroups

precedencegroup PowerPrecedence { higherThan: MultiplicationPrecedence }
infix operator ^^ : PowerPrecedence
func ^^ (radix: Int, power: Int) -> Int {
    return Int(pow(Double(radix), Double(power)))
}

// ...
// Then you can do this...
let i2 = 2 ^^ 3
// ... or
print("2³ = \(2 ^^ 3)") // Prints 2³ = 8

因此,如果要对Float执行此操作,请执行以下操作:infix运算符^^ {} func ^^(基数:Float,幂:Float)-> Float {return Float(pow(Double(radix),Double(power )))}
padapa 2015年

func ^^(radix:Double,power:Double)-> Double {return Double(pow(pow(Double(radix),Double(power))))}
padapa 2015年

3
我发现这并没有达到我的预期,因为优先级已关闭。对于指数运算符,将优先级设置为160(请参见developer.apple.com/library/ios/documentation/Swift/Conceptual/…developer.apple.com/library/ios/documentation/Swift/Conceptual/…),如下所示:infix operator ^^ { precedence 160 } func ^^...依此类推
蒂姆·坎伯

我真的很喜欢这个解决方案,但是对于Swift 3来说,它是行不通的。任何想法如何使其工作?
Vanya

1
func p(_ b: Bool) -> Double { return b?-1:1 }
Grimxn16年

59

除了变量声明存在语法错误之外,这完全符合您的预期。所有你需要做的就是投ab以双和值传递给pow。然后,如果您使用的是2个Int,并且您希望在操作的另一侧返回一个Int,则只需将其转换回Int。

import Darwin 

let a: Int = 3
let b: Int = 3

let x: Int = Int(pow(Double(a),Double(b)))

对于Double和Int类型,此答案最清楚。
Midtownguru

那就是我想要的,谢谢。在Python中,只需3 ** 3。有时,我需要使用Swift解决算法问题,与使用Python相比确实很痛苦。
ChuckZHB

13

有时,Int将a 强制转换为a Double是不可行的解决方案。在某种程度上,这种转换会降低精度。例如,以下代码不会返回您直觉上期望的结果。

Double(Int.max - 1) < Double(Int.max) // false!

如果您需要高幅度的精度,而不必担心负指数(无论如何通常不能用整数来解决),那么对尾递归乘方求平方算法的这种实现是您最好的选择。根据这个SO答案,这是“在非对称密码学中对大量数字进行模幂运算的标准方法”。

// using Swift 5.0
func pow<T: BinaryInteger>(_ base: T, _ power: T) -> T {
    func expBySq(_ y: T, _ x: T, _ n: T) -> T {
        precondition(n >= 0)
        if n == 0 {
            return y
        } else if n == 1 {
            return y * x
        } else if n.isMultiple(of: 2) {
            return expBySq(y, x * x, n / 2)
        } else { // n is odd
            return expBySq(y * x, x * x, (n - 1) / 2)
        }
    }

    return expBySq(1, base, power) 
}

注意:在此示例中,我使用了通用T: BinaryInteger。这样便可以使用IntUInt或任何其他类似整数的类型。


当然,您始终可以将其定义为运算符(如更流行的答案所建议)或作为扩展,Int也可以将这些东西称为免费功能-随心所欲。
mklbtz

看来,此解决方案导致了stackoverflow异常
Vyacheslav

11

如果您确实想要“仅Int”实现,并且不想强制Double执行,则需要实现它。这是一个简单的实现;有更快的算法,但是可以使用:

func pow (_ base:Int, _ power:UInt) -> Int {
  var answer : Int = 1
  for _ in 0..<power { answer *= base }
  return answer
}

> pow (2, 4)
$R3: Int = 16
> pow (2, 8)
$R4: Int = 256
> pow (3,3)
$R5: Int = 27

在实际的实现中,您可能需要进行一些错误检查。


这是一个完全有效的答案。在某些情况下,将Ints转换为Doubles会失去精度,因此这不是Int pow的可行解决方案。只需尝试Double(Int.max - 1) < Double(Int.max)在Swift 3 REPL中运行,您可能会感到惊讶。
mklbtz

2
为了缩短它,您可以通过reduce调用来实现。return (2...power).reduce(base) { result, _ in result * base }
mklbtz

1
也许您可以通过将权力赋予UInt来摆脱前提条件
hashemi

4

更多细节

   infix operator ^^ { associativity left precedence 160 }
   func ^^ (radix: Int, power: Int) -> Int {
       return Int(pow(CGFloat(radix), CGFloat(power)))
   }

迅速-二进制表达式


4

如果您不喜欢运算符重载(尽管对于^^阅读您的代码的人来说解决方案可能很清楚),则可以执行以下快速实现:

let pwrInt:(Int,Int)->Int = { a,b in return Int(pow(Double(a),Double(b))) }
pwrInt(3,4) // 81

4

mklbtz对于平方运算是正确的,因为平方是计算整数幂的标准算法,但是该算法的尾递归实现似乎有些混乱。请参阅http://www.programminglogic.com/fast-exponentiation-algorithms/,以了解通过对C进行平方运算来实现非递归的幂运算的方法。我尝试在此处将其转换为Swift:

func expo(_ base: Int, _ power: Int) -> Int {
    var result = 1

    while (power != 0){
        if (power%2 == 1){
            result *= base
        }
        power /= 2
        base *= base
    }
    return result
}

当然,可以通过创建一个重载的运算符来调用它,并且可以对其进行重写以使其更通用,从而使其可以在实现该IntegerType协议的任何程序上使用。为了使其通用,我可能会从类似

    func expo<T:IntegerType>(_ base: T, _ power: T) -> T {
    var result : T = 1

但是,这可能会被带走。


1
非常好!为了在Swift> 4.0(Xcode 9.0)中进行此操作,您需要使用BinaryIntegerIntegerType不推荐使用。
mklbtz

3

要不就 :

var a:Int = 3
var b:Int = 3
println(pow(Double(a),Double(b)))

3

将答案合并到一组重载的函数中(并使用“ **”代替某些其他语言使用的“ ^^”-对我来说更清楚):

// http://stackoverflow.com/questions/24196689/how-to-get-the-power-of-some-integer-in-swift-language
// Put this at file level anywhere in your project
infix operator ** { associativity left precedence 160 }
func ** (radix: Double, power: Double) -> Double { return pow(radix, power) }
func ** (radix: Int,    power: Int   ) -> Double { return pow(Double(radix), Double(power)) }
func ** (radix: Float,  power: Float ) -> Double { return pow(Double(radix), Double(power)) }

使用Float时,您可能会失去精度。如果使用数字文字以及整数和非整数的混合形式,则默认情况下将以Double结尾。我个人喜欢出于数学/可读性原因而使用数学表达式而不是像pow(a,b)这样的函数的能力,但这就是我自己。

任何会导致pow()引发错误的运算符也会导致这些函数引发错误,因此,无论如何,使用power函数进行代码检查仍然是错误检查的重担。吻,恕我直言。

使用本机的pow()函数可以例如求平方根(2 ** 0.5)或取平方根(2 ** -3 = 1/8)。由于可以使用反指数或分数指数,因此我编写了所有代码以返回pow()函数的默认Double类型,该类型应返回最精确的值(如果我正确地记得文档)。如果需要,可以将其类型转换为Int或Float或其他类型,可能会损失精度。

2 ** -3  = 0.125
2 ** 0.5 = 1.4142135623731
2 ** 3   = 8

pow()不适合小数部分也很重要的大型计算。对我来说,您的答案给出了提示。谢谢:)
马亨德拉

3

事实证明,您也可以使用pow()。例如,您可以使用以下表示10到9。

pow(10, 9)

与一起powpowf()返回float而不是double。我仅在Swift 4和macOS 10.13上进行了测试。


1
如果b <0,pow(a,b)返回NaN; 因此您可以为此添加一个测试:let power =(b> = 0)?pow(a,b):1 / pow(a,-b); 注意必须将a声明为Decimal let a:Decimal = 2; 令b = -3
claude31 '18

2

要计算power(2, n),只需使用:

let result = 2 << (n-1)

1

Swift 4.x版本

precedencegroup ExponentiationPrecedence {
  associativity: right
  higherThan: MultiplicationPrecedence
}

infix operator ^^: ExponentiationPrecedence
public func ^^ (radix: Float, power: Float) -> Float {
  return pow((radix), (power))
}

public func ^^ (radix: Double, power: Double) -> Double {
  return pow((radix), (power))
}

public func ^^ (radix: Int, power: Int) -> Int {
  return NSDecimalNumber(decimal: pow(Decimal(radix), power)).intValue
}

1

在Swift 5中:

extension Int{
    func expo(_ power: Int) -> Int {
        var result = 1
        var powerNum = power
        var tempExpo = self
        while (powerNum != 0){
        if (powerNum%2 == 1){
            result *= tempExpo
        }
        powerNum /= 2
        tempExpo *= tempExpo
        }
        return result
    }
}

这样使用

2.expo(5) // pow(2, 5)

感谢@Paul Buis的回答。


1

Array(重复:a,count:b).reduce(1,*)


1

一个基于Int的pow函数,该函数直接通过Swift 5中以2为底的位移位来计算值:

func pow(base: Int, power: UInt) -> Int {
    if power == 0 { return 1 }
    // for base 2, use a bit shift to compute the value directly
    if base == 2 { return 2 << Int(power - 1) }
    // otherwise multiply base repeatedly to compute the value
    return repeatElement(base, count: Int(power)).reduce(1, *)
}

(确保结果在Int的范围内-这不会检查超出范围的情况)


1

其他答案也不错,但如果愿意的话,Int只要指数为正,也可以扩展使用。

extension Int {   
    func pow(toPower: Int) -> Int {
        guard toPower > 0 else { return 0 }
        return Array(repeating: self, count: toPower).reduce(1, *)
    }
}

2.pow(toPower: 8) // returns 256

0

尝试合并重载,我尝试使用泛型,但无法使其正常工作。我终于想到使用NSNumber而不是尝试重载或使用泛型。这简化为以下内容:

typealias Dbl = Double // Shorter form
infix operator ** {associativity left precedence 160}
func ** (lhs: NSNumber, rhs: NSNumber) -> Dbl {return pow(Dbl(lhs), Dbl(rhs))}

以下代码与上面的功能相同,但执行错误检查以查看参数是否可以成功转换为Doubles。

func ** (lhs: NSNumber, rhs: NSNumber) -> Dbl {
    // Added (probably unnecessary) check that the numbers converted to Doubles
    if (Dbl(lhs) ?? Dbl.NaN) != Dbl.NaN && (Dbl(rhs) ?? Dbl.NaN) != Dbl.NaN {
        return pow(Dbl(lhs), Dbl(rhs))
    } else {
        return Double.NaN
    }
}

-1

迅捷5

我很惊讶,但是我在这里找不到合适的正确解决方案。

这是我的:

enum CustomMath<T: BinaryInteger> {

    static func pow(_ base: T, _ power: T) -> T {
        var tempBase = base
        var tempPower = power
        var result: T = 1

        while (power != 0) {
            if (power % 2 == 1) {
                result *= base
            }
            tempPower = tempPower >> 1
            tempBase *= tempBase
        }
        return result
    }
}

例:

CustomMath.pow(1,1)

-2

我比较喜欢这个

func ^ (left:NSNumber, right: NSNumber) -> NSNumber {
    return pow(left.doubleValue,right.doubleValue)
}
var a:NSNumber = 3
var b:NSNumber = 3 
println( a^b ) // 27

3
那代替了标准的xor运算符。使用此方法会使您的代码对任何不知道您要覆盖单个karat的人以非常意外的方式表现。
wjl 2015年

-3
func calc (base:Int, number:Int) -> Int {
    var answer : Int = base
    for _ in 2...number {answer *= base } 
    return answer
}

例:

calc (2,2)

1
最好的做法是解释代码为什么提供解决方案,而不仅仅是将代码转储为答案。
鲁迪·克肖

1
而且距离正确的幂函数还很远。0作为指数或任何负值怎么办?
macbirdie 2014年

同样,名称“ calc”太笼统而无法在这样的特定操作中使用。cal(2,2)表示您想要对2个数字应用的任何可能的计算... 2 + 2、2-2、2 * 2、2 / 2、2pow2、2root2等
-eharo2
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.