Как получить Power of Some 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 ... что угодно)

если у меня есть два целых числа, и я хочу выполнить операцию питания, как я могу сделать это плавно?

Спасибо!


Эти объявления типов неверны
Эдгар Арутюнян

в большинстве языков нет функции целочисленной мощности по этой причине
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

Итак, если вы хотите сделать это для чисел с плавающей запятой, сделайте это так: 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 ^^... и так далее
Тим Арнольд

Мне это решение очень нравится, но со Swift 3 оно не работает. Есть идеи, как заставить его работать?
Ваня

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

59

Помимо того, что в ваших объявлениях переменных есть синтаксические ошибки, это работает именно так, как вы ожидали. Все, что вам нужно сделать, это привести aи bк Double и передать значения в 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к 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 07

Похоже, это решение приводит к исключению stackoverflow
Вячеслав

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

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
Очень хорошо! Для общего выполнения этого в 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). Из-за возможности использования обратных или дробных показателей я написал весь свой код так, чтобы он возвращал тип 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; так что вы можете добавить тест для этого: let power = (b> = 0)? pow (a, b): 1 / pow (a, -b); обратите внимание, что a должно быть объявлено как Decimal let a: Decimal = 2; let 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

Swift 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
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.