Як отримати силу якоїсь цілої особи на мові 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 000 000 ... що завгодно)

якщо у мене є два цілих числа, і я хочу виконати операцію живлення, як це зробити плавно?

Дякую!


Ці типи декларацій невірні
Едгар Арутьонійський

Більшість мов не мають цілої функції живлення з цієї причини
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, зробите це: оператор інфікування ^^ {} func ^^ (radix: Float, потужність: Float) -> Float {return Float (pow (Double (radix), Double (потужність) )))}
падапа

func ^^ (radix: Double, потужність: Double) -> Double {return Double (pow (Double (radix), Double) (потужність)))}
падаpa

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 воно не працює. Будь-яка ідея, як змусити це працювати?
Ваня

1
func p(_ b: Bool) -> Double { return b?-1:1 }?
Грімксн

59

Крім того, що у ваших змінних декларацій є синтаксичні помилки, це працює саме так, як ви цього очікували. Все, що вам потрібно зробити, - це передавати aта bподвоювати та передавати значення pow. Потім, якщо ви працюєте з 2 інтами і хочете 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.
ЧакЖБ

13

Іноді переведення Intна «a» Doubleне є прийнятним рішенням. На деяких величинах відбувається втрата точності при цьому перетворенні. Наприклад, наступний код не повертає те, що ви могли інтуїтивно очікувати.

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

Якщо вам потрібна точність на великій величині, і вам не потрібно турбуватися про негативні показники, які взагалі не можна вирішити цілими числами - тоді ця реалізація алгоритму хвостово-рекурсивної експоненціації шляхом квадратування - найкраща ставка. Відповідно до цієї відповіді ТА , це "стандартний метод проведення модульної експоненції для величезних чисел в асиметричній криптографії".

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

Здається, це рішення призводить до винятку стаковерху
В’ячеслав

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 в парні втрачає точність, і тому це не є життєздатним рішенням для 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
хешемі


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), ви хочете використовувати 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 (), також призведуть до помилок, і ці функції переносять помилку, тому тягар перевірки помилок все ще лежить на коді за допомогою функції живлення. KISS, ІМХО.

Використання нативної функції pow () дозволяє, наприклад, взяти квадратні корені (2 ** 0,5) або обернені (2 ** -3 = 1/8). Через можливість використовувати зворотні або дробові експоненти, я написав увесь свій код, щоб повернути типовий подвійний тип функції 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)? pow (a, b): 1 / pow (a, -b); зауважимо, що a повинен бути оголошений як десятковий, нехай 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))}

Наступний код є тією ж функцією, що і вище, але здійснює перевірку помилок, щоб перевірити, чи вдало параметри можуть бути перетворені в подвійні.

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
Це добре, щоб пояснити, чому ваш код пропонує рішення, а не просто скидати код у відповідь.
Руді Кершо

1
І це далеко не правильна функція живлення. Як щодо 0 як показника або будь-якого негативного значення.
macbirdie

Крім того, назва "calc" занадто загальне, щоб використовуватись у такій конкретній операції. Cal (2,2) може означати будь-який можливий розрахунок, який ви хочете застосувати до 2 чисел ... 2 + 2, 2-2, 2 * 2, 2/2, 2pow2, 2root2 тощо
eharo2
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.