จะรับพลังของ Integer บางตัวในภาษา 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

ภาษาส่วนใหญ่ไม่มีฟังก์ชันกำลังจำนวนเต็มเนื่องจากเหตุผลนี้
phuclv

1
บันทึกของ @ phuclv ชี้ให้เห็นถึงการอภิปรายที่ยอดเยี่ยมในหัวข้อนี้ ฉันจะเปลี่ยนข้อความในลิงก์เป็น "เหตุผลเหล่านี้"
eharo2

คำตอบ:


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

ถ้าคุณต้องการทำสิ่งนี้สำหรับ Floats คุณจะทำสิ่งนี้หรือไม่: infix operator ^^ {} func ^^ (radix: Float, power: Float) -> Float {return Float (pow (Double (radix), Double (power )))}
padapa

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

3
ฉันพบว่าสิ่งนี้ไม่ได้ทำงานอย่างที่ฉันคาดไว้เพราะลำดับความสำคัญถูกปิด สำหรับตัวดำเนินการเลขชี้กำลังให้ตั้งค่าลำดับความสำคัญเป็น 160 (โปรดดูที่developer.apple.com/library/ios/documentation/Swift/Conceptual/…และdeveloper.apple.com/library/ios/documentation/Swift/Conceptual/… ) ดังนี้: infix operator ^^ { precedence 160 } func ^^... และอื่น ๆ
Tim Arnold

ฉันชอบวิธีนี้มาก แต่กับ Swift 3 มันไม่ได้ผล มีความคิดอย่างไรที่จะทำให้มันใช้งานได้?
Vanya

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

59

นอกเหนือจากนั้นการประกาศตัวแปรของคุณมีข้อผิดพลาดทางไวยากรณ์การดำเนินการนี้จะเป็นไปตามที่คุณคาดหวังไว้ ทั้งหมดที่คุณต้องทำคือการโยนaและดับเบิลและส่งค่าไปb powจากนั้นหากคุณกำลังทำงานกับ 2 Ints และคุณต้องการให้ 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 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. เพื่อให้คุณสามารถใช้IntหรือUIntหรือประเภทอื่น ๆ ที่คล้ายจำนวนเต็ม


และแน่นอนคุณสามารถกำหนดสิ่งนี้เป็นตัวดำเนินการได้ตลอดเวลา (ตามที่คำตอบยอดนิยมแนะนำ) หรือส่วนขยายIntหรือคุณสามารถให้สิ่งเหล่านั้นเรียกฟังก์ชันนี้ได้ฟรีไม่ว่าคุณจะต้องการอะไรก็ตาม
mklbtz

ดูเหมือนว่าโซลูชันนี้นำไปสู่ข้อยกเว้น stackoverflow
Vyacheslav

11

หากคุณต้องการใช้งาน 'Int only' จริงๆและไม่ต้องการบังคับให้ไป / กลับ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

3
หากต้องการย่อให้สั้นลงคุณสามารถใช้สิ่งนี้ด้วยการreduceโทร return (2...power).reduce(base) { result, _ in result * base }
mklbtz

1
บางทีคุณสามารถกำจัดเงื่อนไขเบื้องต้นได้ด้วยการเพิ่มพลังให้กับ UInt
hashemi


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
ดีมาก! การทำเช่นนี้โดยทั่วไปในสวิฟท์> 4.0 (Xcode 9.0), BinaryIntegerคุณต้องการที่จะใช้ IntegerTypeเลิกใช้งานแล้ว
mklbtz


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 () แสดงข้อผิดพลาดก็จะทำให้ฟังก์ชันเหล่านี้แสดงข้อผิดพลาดได้เช่นกันดังนั้นภาระในการตรวจสอบข้อผิดพลาดจึงยังคงอยู่กับโค้ดที่ใช้ฟังก์ชันพาวเวอร์อยู่ดี จูบ IMHO

การใช้ฟังก์ชัน native pow () ช่วยให้สามารถใช้รากที่สอง (2 ** 0.5) หรือผกผัน (2 ** -3 = 1/8) เนื่องจากความเป็นไปได้ที่จะใช้เลขชี้กำลังผกผันหรือเศษส่วนฉันจึงเขียนโค้ดทั้งหมดของฉันเพื่อส่งคืนค่าเริ่มต้นประเภท Double ของฟังก์ชัน pow () ซึ่งควรให้ความแม่นยำสูงสุด (ถ้าฉันจำเอกสารประกอบได้อย่างถูกต้อง) หากจำเป็นก็สามารถพิมพ์ลงไปที่ Int หรือ Float หรืออะไรก็ได้ซึ่งอาจสูญเสียความแม่นยำ

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

pow () ไม่เหมาะสำหรับการคำนวณจำนวนมากซึ่งส่วนที่เป็นเศษส่วนก็มีความสำคัญเช่นกัน สำหรับฉันคำตอบของคุณให้คำใบ้ ขอบคุณ :)
Mahendra

3

pow()มันจะเปิดออกคุณยังสามารถใช้ ตัวอย่างเช่นคุณสามารถใช้สิ่งต่อไปนี้เพื่อแสดง 10 ถึง 9

pow(10, 9)

พร้อมกับpow, powf()ส่งกลับแทนfloat doubleฉันได้ทดสอบสิ่งนี้กับ Swift 4 และ macOS 10.13 เท่านั้น


1
pow (a, b) ส่งคืน NaN ถ้า b <0; ดังนั้นคุณสามารถเพิ่มการทดสอบสำหรับสิ่งนี้: ให้ power = (b> = 0)? ธาร (a, b): 1 / pow (a, -b); โปรดทราบว่าต้องประกาศเป็นทศนิยมให้ a: Decimal = 2; ให้ b = -3
claude31


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

ฟังก์ชัน Pow ตาม Int ที่คำนวณค่าโดยตรงผ่านการกะบิตสำหรับฐาน 2 ใน 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, *)
}

(ตรวจสอบให้แน่ใจว่าผลลัพธ์อยู่ในช่วงของ 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 มาตรฐาน การใช้สิ่งนี้จะทำให้โค้ดของคุณทำงานในรูปแบบที่ไม่คาดคิดสำหรับใครก็ตามที่ไม่รู้ว่าคุณกำลังเอาชนะกะรัตเดียว
wjl

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

ตัวอย่าง:

calc (2,2)

1
เป็นแนวทางปฏิบัติที่ดีในการอธิบายว่าเหตุใดโค้ดของคุณจึงเสนอวิธีแก้ปัญหาแทนที่จะทิ้งโค้ดลงในคำตอบ
Rudi Kershaw

1
และยังห่างไกลจากฟังก์ชันการใช้พลังงานที่ถูกต้อง แล้ว 0 เป็นเลขชี้กำลังหรือค่าลบใด ๆ
macbirdie

นอกจากนี้ชื่อ "calc" ยังเป็นชื่อสามัญเกินไปที่จะใช้ในการดำเนินการเฉพาะเช่นนี้ .. cal (2,2) อาจหมายถึงการคำนวณที่เป็นไปได้ที่คุณต้องการใช้กับ 2 ตัวเลข ... 2 + 2, 2-2, 2 * 2, 2/2, 2pow2, 2root2 ฯลฯ
eharo2
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.