Elevar ao poder


12

Desafio

Desafio é escrever um programa que leva positivos números ae um zero número be saídas a^b(a elevado à potência b). Você só pode usar + - * / abs()como funções / operadores matemáticos. Eles podem ser aplicados apenas a valores escalares, mas não a listas ou matrizes inteiras.

Exemplos:

1.234 ^ 5.678 = 3.29980
4.5   ^ 4.5   = 869.874
4.5   ^-4.5   = 0.00114959

Relevante: http://xkcd.com/217/

Detalhes

Você pode escrever uma função ou uma construção semelhante para usar no console. Se você não pode usar a entrada do console, pode assumir que os dois números são salvos em variáveis ​​e saídos via saída padrão ou gravando em um arquivo. A saída deve estar correta para pelo menos 4 dígitos significativos. Você pode assumir que ambos ae bsão diferentes de zero. Um tempo de execução significativamente maior que 1 minuto não é aceitável. O menor número de bytes vencerá. Por favor, explique seu programa e seu algoritmo.

EDIT: Apenas bases positivas devem ser consideradas. Você pode assumir a>0. Esteja ciente de que os dois números não precisam ser inteiros !!!


3
Você está nos pedindo para aumentar para um poder decimal? Como por exemplo, 4,5 ^ 4,5?
fuandon 4/08/14

1
Isso significa que também precisamos gerar números imaginários se a base for negativa?
bebe

1
Qual deve ser a saída -0.5 ** 0.5?
Dennis

Ok, eu não pensei nesse caso, obrigado: Bases negativas não devem ser corretamente implementadas. @fuandon exatamente, números reais podem ter decimais (pelo menos na maioria das linguagens de programação =)
flawr

Eu gostaria de adicionar um caso de teste com b <0: `4.5 ^ -4.5 = 0,0011496'
edc65

Respostas:


3

Python, 77

Como em algumas outras respostas, isso é baseado em log e exp. Mas as funções são calculadas resolvendo numericamente equações diferenciais ordinárias.

def f(a,b,y=1):
 if a<1:a=1/a;b=-b
 while a>1:a/=1e-7+1;y*=b*1e-7+1
 return y

Satisfaz os requisitos? Para os exemplos na pergunta, sim. Para um grande, vai demorar muito tempo. Para grandes a ou b, ele se tornará impreciso.

Exemplos:

a            b            f(a, b)      pow(a, b)      <1e-5 rel error?
       1.234        5.678       3.2998       3.2998   OK
         4.5          4.5      869.873      869.874   OK
         4.5         -4.5   0.00114959   0.00114959   OK
         0.5          0.5     0.707107     0.707107   OK
         0.5         -0.5      1.41421      1.41421   OK
          80            5  3.27679e+09   3.2768e+09   OK
     2.71828      3.14159      23.1407      23.1407   OK

Atualização: flawr pediu mais detalhes sobre a matemática, então aqui está. Eu considerei os seguintes problemas de valor inicial:

  • x '(t) = x (t), com x (0) = 1. A solução é exp (t).
  • y '(t) = por (t), com y (0) = 1. A solução é exp (bt).

Se eu puder encontrar o valor de t tal que x (t) = a, então terei y (t) = exp (bt) = a ^ b. A maneira mais simples de resolver numericamente um problema de valor inicial é o método de Euler . Você calcula a derivada que a função deve ter e, em seguida, dá um passo na direção da derivada e é proporcional a ela, mas dimensionada por uma pequena constante. Então é isso que eu faço, dê pequenos passos até que x seja tão grande quanto ae, em seguida, veja o que y é naquele momento. Bem, foi assim que pensei. No meu código, t nunca é explicitamente calculado (é 1e-7 * o número de etapas do loop while), e salvei alguns caracteres fazendo os cálculos de x com um.


Parece ótimo, estou feliz em ver outra abordagem diferente! Você pode nos contar um pouco mais sobre essas equações diferenciais? Eu sei geralmente o que eles são, mas eu não era capaz de descobrir como você programa usa-los =)
flawr

@ flawr: OK, atualizei com mais alguns detalhes sobre a matemática.
reaqueceu

6

JavaScript (E6) 155174191

Editar 2 Conforme sugerido por @bebe, usando a função recursiva (executa pior, mas menor)
Função R alterada ligeiramente para evitar 'recursão demais' Conjunto de
testes adicionado. A função tem bom desempenho para bases <3000 e expoente no intervalo -50..50.
Editar Golfed mais e melhor precisão

Qualquer número real pode ser aproximado com um número racional (e os números 'reais' padrão do IEEE armazenam os racionais de fato). Qualquer número racional pode ser expresso como uma fração a / b com números inteiros aeb. x ^ (a / b) é a raiz b de (x ^ a) ou (raiz b de x) ^ a. A exponenciação de número inteiro é bastante fácil ao quadrado. Raiz inteira pode ser aproximada usando métodos numéricos.

Código

P=(x,e)=>(
  f=1e7,e<0&&(x=1/x,e=-e),
  F=(b,e,r=1)=>e?F(b*b,e>>1,e&1?r*b:r):r,
  R=(b,e,g=1,y=1e-30,d=(b/F(g,e-1)-g)/e)=>d>y|d<-y?R(b,e,g+d,y/.99):g,
  F(R(x,f),e*f)
)

Teste no console do FireFox ou FireBug

for (i=0;i<100;i++)
{
  b=Math.random()*3000
  e=Math.random()*100-50
  p1=Math.pow(b,e) // standard power function, to check
  p2=P(b,e)
  d=(p1-p2)/p1 // relative difference
  if (!isFinite(p2) || d > 0.001) 
    console.log(i, b, e, p1, p2, d.toFixed(3))
}

Bom trabalho, não muito precisos, mas o algoritmo é agradável =)
flawr

Você pode explicar o que isso e&1&&(r*=b)faz, exceto multiplicar rpor b?
flawr

1
@flawrif(e&1 != 0) r *= b
bebe

Obrigado, eu não estava ciente de que exploram, mas parece ser um agradável para jogar golfe =)
flawr

1
aqui está o código de trabalho: P=(x,e)=>(F=(b,e,r=1)=>e?F(b*b,e>>1,e&1?r*b:r):r,R=(b,e,g=1,y=1e-16,d=(b/F(g,e-1)-g)/e)=>d>y|d<-y?R(b,e,g+d):g,e<0&&(x=1/x,e=-e),f=1<<24,F(R(x,f),e*f))(i deve estar cansado)
bebe

6

Haskell, 85 90

Algoritmo exp-log padrão. Agora com um nome diferente, eliminando mais alguns caracteres:

a%b|a>1=1/(1/a)%b|0<1=sum$scanl((/).((-b*foldr1(\n b->(1-a)*(b+1/n))c)*))1c
c=[1..99]

raiseagora é chamado (%)ou %em notação infix, mesmo fazendo seu uso consumir menos bytes:4.5%(-4.5)

A versão ungolfed também usa apenas 172 bytes:

raise a b | a > 1     = 1 / raise (1/a) b
          | otherwise = expo (-b* ln (1-a))

ln x = foldr1 (\n a -> x*a+x/n) [1..99]

expo x = sum $ scanl ((/) . (x*)) 1 [1..99]

4

JS (ES6), 103 bytes

t=(x,m,f)=>{for(r=i=s=u=1;i<1<<7+f;r+=s/(u=i++*(f?1:u)))s*=m;return r};e=(a,b)=>t(b,t(a,1-1/a,9)*b-b,0)

Exemplos :

e(1.234,5.678) = 3.299798925315965
e(4.5,4.5)     = 869.8739233782269
e(4.5,-4.5)    = 0.0011495918812070608

Use a série Taylor.
b^x = 1 + ln(b)*x/1! + (ln(b)*x)^2/2! + (ln(b)*x)^3/3! + (ln(b)*x)^4/4! + ...
com aproximação natural do logaritmo :
ln(b) = (1-1/x) + (1-1/x)^2/2 + (1-1/x)^3/3 + (1-1/x)^4/4 + ...

Usei 128 iterações para calcular b^x(mais iterações são difíceis devido ao fatorial) e 262144 iterações paraln(b)


Talvez você deve golf menos, mas adicionar mais precisão: e(80,5) ->1555962210.2240903- deve ser 3276800000
edc65

@ edc65, você está certo, consertado por mais 5 caracteres.
Michael M.

1
É muito bom ver algumas abordagens diferentes!
flawr

3

golflua 120

Eu uso o fato de que

a^b = exp(log(a^b)) = exp(b*log(a))

e escreveu os meus próprios loge expfunções. Os valores ae bprecisam ser inseridos nas novas linhas quando executados no terminal:

\L(x)g=0~@ i=1,50 c=(x-1)/x~@j=2,i c = c*(x-1)/x$g=g+c/i$~g$\E(x)g=1;R=1e7~@i=1,R g=g*(1+x/R)$~g$a=I.r()b=I.r()w(E(b*L(a)))

Amostras de execuções:

4.5, 4.5  ==> 869.87104890175
4.5, -4.5 ==> 0.0011495904124065
3.0, 2.33 ==> 12.932794624815
9.0, 0.0  ==> 1
2.0, 2.0  ==> 3.9999996172672

Uma versão não-gasta de Lua é,

-- returns log
function L(x)
   g = 0
   for i=1,50 do
      c=(x-1)/x
      for j=2,i do
         c = c*(x-1)/x
      end
      g = g + c/i
   end
   return g
end

-- returns exp
function E(x)
   g=1;L=9999999
   for i=1,L do
      g=g*(1+x/L)
   end
   return g
end

a=io.read()
b=io.read()

print(E(b*L(a)))
print(a^b)

Você pode fornecer alguns exemplos de saídas?
flawr

@flawr: Suponho que posso ... e agora feito
Kyle Kanos
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.