Làm thế nào để có được Sức mạnh của một số Integer trong ngôn ngữ Swift?


102

Gần đây tôi đang học nhanh, nhưng tôi gặp một vấn đề cơ bản không thể tìm ra câu trả lời

Tôi muốn lấy một cái gì đó giống như

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

nhưng hàm pow chỉ có thể hoạt động với số nhân đôi, nó không hoạt động với số nguyên và tôi thậm chí không thể ép kiểu int thành nhân đôi bởi một cái gì đó như Double (a) hoặc a.double () ...

Tại sao nó không cung cấp sức mạnh của số nguyên? nó chắc chắn sẽ trả về một số nguyên mà không có sự mơ hồ! và Tại sao tôi không thể chuyển một số nguyên thành một số kép? nó chỉ thay đổi 3 thành 3.0 (hoặc 3.00000 ... bất cứ điều gì)

Nếu tôi có hai số nguyên và tôi muốn thực hiện thao tác cấp nguồn, làm cách nào tôi có thể thực hiện nó một cách suôn sẻ?

Cảm ơn!


Những tờ khai loại là sai
Edgar Aroutiounian

hầu hết các ngôn ngữ không có một chức năng điện nguyên do lý do này
phuclv

1
Ghi chú của @ phuclv chỉ ra một cuộc thảo luận tuyệt vời về chủ đề này. Tôi sẽ thay đổi văn bản trong liên kết thành "những lý do này"
eharo2

Câu trả lời:


78

Nếu bạn thích, bạn có thể tuyên bố một infix operatorđể làm điều đó.

// 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

Tôi đã sử dụng hai dấu mũ để bạn vẫn có thể sử dụng toán tử XOR .

Cập nhật cho Swift 3

Trong Swift 3, "số ma thuật" precedenceđược thay thế bằng 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

Vì vậy, nếu bạn muốn làm điều này cho Floats, bạn sẽ thực hiện điều này: toán tử infix ^^ {} func ^^ (cơ số: Float, power: Float) -> Float {return Float (pow (Double (cơ số), Double (power )))}
padapa

Func ^^ (radix: đôi, sức mạnh: Đôi) -> đúp {return đôi (pow (đôi (radix), đôi (sức mạnh)))}
padapa

3
Tôi thấy điều này không hoàn toàn hoạt động như tôi mong đợi vì quyền ưu tiên đã bị tắt. Đối với toán tử lũy thừa, hãy đặt ưu tiên thành 160 (xem developer.apple.com/library/ios/documentation/Swift/Conceptual/…developer.apple.com/library/ios/documentation/Swift/Conceptual/… ) như sau: infix operator ^^ { precedence 160 } func ^^... và như vậy
Tim Camber

Tôi thực sự thích giải pháp này, nhưng với Swift 3, nó không hoạt động. Bất kỳ ý tưởng làm thế nào để làm cho nó hoạt động?
Vanya

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

59

Ngoài việc khai báo biến của bạn có lỗi cú pháp, điều này hoạt động chính xác như bạn mong đợi. Tất cả những gì bạn phải làm là ép kiểu avà Nhân bđôi và chuyển các giá trị cho pow. Sau đó, nếu bạn đang làm việc với 2 Ints và bạn muốn một Int trở lại ở phía bên kia của thao tác, chỉ cần chuyển trở lại Int.

import Darwin 

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

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

Câu trả lời này là rõ ràng nhất với loại Double và Int.
midtownguru

Đó là những gì tôi muốn, cảm ơn. Trong Python, chỉ 3 ** 3. Đôi khi, tôi cần giải quyết vấn đề thuật toán bằng Swift, nó thực sự khó khăn so với sử dụng Python.
ChuckZHB

13

Đôi khi, truyền một Intđến a Doublekhông phải là một giải pháp khả thi. Ở một số cường độ, việc chuyển đổi này bị mất độ chính xác. Ví dụ: đoạn mã sau không trả về những gì bạn có thể mong đợi bằng trực giác.

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

Nếu bạn cần độ chính xác ở độ lớn cao và không cần phải lo lắng về số mũ âm - điều này thường không thể được giải quyết bằng số nguyên - thì việc triển khai thuật toán lũy thừa đệ quy theo bình phương này là lựa chọn tốt nhất của bạn. Theo câu trả lời SO này , đây là "phương pháp tiêu chuẩn để thực hiện tính lũy thừa mô-đun cho các số lớn trong mật mã không đối xứng."

// 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) 
}

Lưu ý: trong ví dụ này, tôi đã sử dụng một từ chung T: BinaryInteger. Đây là vì vậy bạn có thể sử dụng Inthoặc UInthoặc bất kỳ loại nguyên khác giống.


Và tất nhiên bạn luôn có thể định nghĩa đây là một toán tử (như các câu trả lời phổ biến hơn đề xuất) hoặc một phần mở rộng cho Inthoặc bạn có thể gọi những thứ đó là chức năng miễn phí - bất cứ điều gì trái tim bạn mong muốn.
mklbtz

Có vẻ như, giải pháp này dẫn đến ngoại lệ stackoverflow
Vyacheslav

11

Nếu bạn thực sự muốn triển khai 'Int only' và không muốn ép buộc đến / từDouble , bạn sẽ cần triển khai nó. Đây là một triển khai tầm thường; có các thuật toán nhanh hơn nhưng điều này sẽ hoạt động:

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

Trong một triển khai thực tế, bạn có thể muốn kiểm tra một số lỗi.


Đây là một câu trả lời hoàn toàn hợp lệ. Có một số trường hợp chuyển đổi Ints thành Double mất độ chính xác và vì vậy đó không phải là giải pháp khả thi cho Int pow. Chỉ cần thử chạy Double(Int.max - 1) < Double(Int.max)trong Swift 3 REPL và bạn có thể ngạc nhiên.
mklbtz

2
Để rút ngắn thời gian, bạn có thể thực hiện điều này bằng một reducecuộc gọi. return (2...power).reduce(base) { result, _ in result * base }
mklbtz

1
Có lẽ bạn có thể thoát khỏi những điều kiện tiên quyết bằng cách làm cho sức mạnh một uint
Hashemi


4

Nếu bạn không muốn quá tải toán tử (mặc dù ^^giải pháp có thể rõ ràng cho ai đó đang đọc mã của bạn), bạn có thể thực hiện nhanh chóng:

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

4

mklbtz đúng về lũy thừa bằng bình phương là thuật toán tiêu chuẩn để tính lũy thừa số nguyên, nhưng việc triển khai đệ quy đuôi của thuật toán có vẻ hơi khó hiểu. Xem http://www.programminglogic.com/fast-exponentiation-algorithm/ để biết cách triển khai tính lũy thừa không đệ quy bằng cách bình phương trong C. Tôi đã cố dịch nó sang Swift tại đây:

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
}

Tất nhiên, điều này có thể được tạo ra bằng cách tạo ra một toán tử quá tải để gọi nó và nó có thể được viết lại để làm cho nó chung chung hơn để nó hoạt động trên bất kỳ thứ gì triển khai IntegerTypegiao thức. Để làm cho nó chung chung, có lẽ tôi sẽ bắt đầu với những thứ như

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

Nhưng, điều đó có lẽ đang bị cuốn trôi.


1
Rất đẹp! Để làm điều này một cách chung chung trong Swift> 4.0 (Xcode 9.0), bạn muốn sử dụng BinaryInteger. IntegerTypekhông được dùng nữa.
mklbtz

3

Hoặc chỉ:

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

3

Kết hợp các câu trả lời thành một tập hợp các hàm được nạp chồng (và sử dụng "**" thay vì "^^" như một số ngôn ngữ khác sử dụng - rõ ràng hơn đối với tôi):

// 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)) }

Khi sử dụng Float, bạn có thể mất độ chính xác. Nếu sử dụng các ký tự số và kết hợp số nguyên và không phải số nguyên, bạn sẽ kết thúc bằng Double theo mặc định. Cá nhân tôi thích khả năng sử dụng một biểu thức toán học thay vì một hàm như pow (a, b) vì lý do kiểu cách / dễ đọc, nhưng đó chỉ là tôi.

Bất kỳ toán tử nào gây ra lỗi pow () cũng sẽ khiến các hàm này gặp lỗi, do đó, gánh nặng của việc kiểm tra lỗi vẫn nằm ở mã sử dụng hàm nguồn. HÔN, IMHO.

Sử dụng hàm pow () gốc cho phép lấy căn bậc hai (2 ** 0,5) hoặc nghịch đảo (2 ** -3 = 1/8). Do khả năng sử dụng số mũ nghịch đảo hoặc phân số, tôi đã viết tất cả mã của mình để trả về kiểu Double mặc định của hàm pow (), loại này sẽ trả về độ chính xác cao nhất (nếu tôi nhớ đúng tài liệu). Nếu cần, điều này có thể được nhập xuống Int hoặc Float hoặc bất cứ thứ gì, có thể mất độ chính xác.

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

pow () không thích hợp cho phép tính lớn, nơi mà phần phân số cũng rất quan trọng. Đối với tôi câu trả lời của bạn cho một gợi ý. Cảm ơn :)
Mahendra

3

Nó chỉ ra bạn cũng có thể sử dụng pow(). Ví dụ, bạn có thể sử dụng như sau để diễn đạt 10 đến 9.

pow(10, 9)

Cùng với pow, powf()trả về a floatthay vì a double. Tôi chỉ mới thử nghiệm điều này trên Swift 4 và macOS 10.13.


1
pow (a, b) trả về NaN nếu b <0; vì vậy bạn có thể thêm một bài kiểm tra cho điều này: let power = (b> = 0)? pow (a, b): 1 / pow (a, -b); lưu ý rằng a phải được khai báo là Decimal let a: Decimal = 2; hãy để b = -3
claude31

2

Để tính toán power(2, n), chỉ cần sử dụng:

let result = 2 << (n-1)

1

Phiên bản 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

Trong 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
    }
}

Sử dụng như thế này

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

Cảm ơn câu trả lời của @Paul Buis.



1

Một hàm pow dựa trên Int tính toán giá trị trực tiếp thông qua dịch chuyển bit cho cơ sở 2 trong Swift 5:

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, *)
}

(Đảm bảo kết quả nằm trong phạm vi Int - điều này không kiểm tra trường hợp nằm ngoài giới hạn)


1

Các câu trả lời khác rất tuyệt nhưng nếu được ưu tiên, bạn cũng có thể làm điều đó với Intphần mở rộng miễn là số mũ dương.

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

Cố gắng kết hợp quá tải, tôi đã cố gắng sử dụng thuốc chung nhưng không thể làm cho nó hoạt động. Cuối cùng tôi đã tìm ra cách sử dụng NSNumber thay vì cố gắng quá tải hoặc sử dụng generic. Điều này đơn giản hóa thành những điều sau:

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

Đoạn mã sau là chức năng tương tự như trên nhưng thực hiện kiểm tra lỗi để xem liệu các thông số có thể được chuyển đổi thành Double thành công hay không.

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

Swift 5

Tôi đã rất ngạc nhiên, nhưng tôi không tìm thấy một giải pháp chính xác thích hợp ở đây.

Cái này của tôi ư:

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
    }
}

Thí dụ:

CustomMath.pow(1,1)

-2

Tôi thích cái này hơn

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
Điều đó thay thế toán tử xor tiêu chuẩn. Sử dụng điều này sẽ làm cho mã của bạn hoạt động theo một cách rất bất ngờ đối với bất kỳ ai không biết bạn đang ghi đè một karat duy nhất.
wjl

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

Thí dụ:

calc (2,2)

1
Bạn nên giải thích lý do tại sao mã của bạn đưa ra một giải pháp, thay vì chỉ đưa mã vào một câu trả lời.
Rudi Kershaw

1
Và nó không phải là một chức năng quyền lực chính xác. Còn về 0 như một số mũ hoặc bất kỳ giá trị âm nào.
macbirdie

Ngoài ra, tên 'calc "quá chung chung để được sử dụng trong một hoạt động cụ thể như vậy .. cal (2,2) có thể có nghĩa là bất kỳ phép tính khả thi nào bạn muốn áp dụng cho 2 số ... 2 + 2, 2-2, 2 * 2, 2/2, 2pow2, 2root2, v.v.
eharo2
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.