Erhebe dich zur Macht

12

Herausforderung

Die Herausforderung besteht darin, ein Programm zu schreiben, das eine positive Zahl aund eine Zahl ungleich Null annimmt bund ausgibt a^b(a wird zur Potenz erhoben b). Sie können nur + - * / abs()als mathematische Funktionen / Operatoren verwenden. Diese können nur auf skalare Werte angewendet werden, nicht jedoch auf ganze Listen oder Arrays.

Beispiele:

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

Relevant: http://xkcd.com/217/

Einzelheiten

Sie können eine Funktion oder ein ähnliches Konstrukt für die Verwendung in der Konsole schreiben. Wenn Sie die Konsoleneingabe nicht verwenden können, können Sie davon ausgehen, dass beide Nummern in Variablen und Ausgaben über die Standardausgabe oder das Schreiben in eine Datei gespeichert werden. Die Ausgabe muss auf mindestens 4 Stellen genau sein. Sie können davon ausgehen, dass beide aund bungleich Null sind. Eine Laufzeit von deutlich mehr als 1 Minute ist nicht akzeptabel. Die geringste Anzahl von Bytes gewinnt. Bitte erläutern Sie Ihr Programm und Ihren Algorithmus.

EDIT: Nur positive Basen müssen berücksichtigt werden. Sie können davon ausgehen a>0. Beachten Sie, dass beide Zahlen keine ganzen Zahlen sein müssen !!!

Fehler
quelle
3
Bitten Sie uns, auf eine Dezimalstelle anzuheben? Wie gesagt, 4.5 ^ 4.5?
Fuandon
1
Bedeutet dies, dass wir auch imaginäre Zahlen ausgeben müssen, wenn die Basis negativ ist?
Bebe
1
Wofür soll die Ausgabe -0.5 ** 0.5sein?
Dennis
Ok, ich habe an diesen Fall nicht gedacht, danke: Negative Basen müssen nicht korrekt implementiert werden. @ Fuandon genau, können reelle Zahlen Dezimalstellen haben (zumindest in den meisten Programmiersprachen =)
flawr
Ich möchte einen Testfall mit b <0: `4.5 ^ -4.5 = 0.0011496 '
edc65

Antworten:

3

Python, 77

Wie bei einigen anderen Antworten basiert dies auf log und exp. Die Funktionen werden jedoch durch numerisches Lösen gewöhnlicher Differentialgleichungen berechnet.

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

Entspricht es den Anforderungen? Für die Beispiele in der Frage ja. Für große a wird es sehr lange dauern. Für große a oder b wird es ungenau.

Beispiele:

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

Update: flawr bat um mehr Details zur Mathematik, also los geht's. Ich habe folgende Anfangswertprobleme betrachtet:

  • x '(t) = x (t) mit x (0) = 1. Die Lösung ist exp (t).
  • y '(t) = durch (t) mit y (0) = 1. Die Lösung ist exp (bt).

Wenn ich den Wert von t so finde, dass x (t) = a, dann habe ich y (t) = exp (bt) = a ^ b. Der einfachste Weg, ein Anfangswertproblem numerisch zu lösen, ist die Euler-Methode . Sie berechnen die Ableitung, die die Funktion haben soll, und machen dann einen Schritt in Richtung der Ableitung und proportional dazu, aber skaliert durch eine winzige Konstante. Also mache ich das, mache winzige Schritte, bis x so groß wie a ist, und sehe dann, was y zu dieser Zeit ist. Nun, so habe ich es mir vorgestellt. In meinem Code wird t nie explizit berechnet (es ist 1e-7 * die Anzahl der Schritte der while-Schleife), und ich habe einige Zeichen gespeichert, indem ich stattdessen die Berechnungen für x mit a durchgeführt habe.

wiedergewärmt
quelle
Das sieht gut aus, ich freue mich über einen anderen Ansatz! Können Sie uns etwas mehr über diese Differentialgleichungen erzählen? Ich weiß im Allgemeinen, was sie sind, aber ich konnte nicht herausfinden, wie Sie sie programmieren =)
Fehler
@flawr: OK, ich habe ein paar Details zur Mathematik hinzugefügt.
Wiedererwärmung
6

JavaScript (E6) 155 174 191

Edit 2 Wie von @bebe vorgeschlagen, mit rekursiver Funktion (schlechter, aber kürzer).
Leicht geänderte R-Funktion, um zu viel Rekursion zu vermeiden.
Test-Suite hinzugefügt. Die Funktion funktioniert gut für Basen <3000 und Exponenten im Bereich -50..50. Mehr und bessere Präzision golfen
bearbeiten

Jede reelle Zahl kann mit einer rationalen Zahl angenähert werden (und IEEE-Standard-'reale'-Zahlen speichern tatsächlich Rationen). Jede rationale Zahl kann als Bruch a / b mit den ganzen Zahlen a und b ausgedrückt werden. x ^ (a / b) ist Wurzel b von (x ^ a) oder (Wurzel b von x) ^ a. Die ganzzahlige Potenzierung ist durch Quadrieren recht einfach. Die Ganzzahlwurzel kann mit numerischen Methoden approximiert werden.

Code

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

Test In FireFox oder Firebug - Konsole

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))
}
edc65
quelle
Gute Arbeit, nicht sehr genau, aber der Algorithmus ist nett =)
Fehler
Können Sie erklären, was dies e&1&&(r*=b)außer der Multiplikation rmit bedeutet b?
Fehler
1
@flawrif(e&1 != 0) r *= b
bebe
Danke, mir war dieser Exploit nicht bekannt, aber es scheint ein schöner zu sein, um Golf zu spielen =)
flawr
1
Hier ist der Arbeitscode: 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))(Ich muss müde sein)
Be
6

Haskell, 85 90

Standard-Exp-Log-Algorithmus. Jetzt mit anderem Namen, ein paar Zeichen mehr abschneiden:

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]

raiseheißt jetzt (%)oder %in der Infixnotation und verbraucht sogar weniger Bytes:4.5%(-4.5)

Die ungolfed Version verwendet auch nur 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]
TheSpanishInquisition
quelle
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)

Beispiele:

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

Verwenden Sie Taylor-Serien.
b^x = 1 + ln(b)*x/1! + (ln(b)*x)^2/2! + (ln(b)*x)^3/3! + (ln(b)*x)^4/4! + ...
mit natürlicher Logarithmusnäherung :
ln(b) = (1-1/x) + (1-1/x)^2/2 + (1-1/x)^3/3 + (1-1/x)^4/4 + ...

Ich habe 128 Iterationen zum Berechnen verwendet b^x(mehr Iterationen sind aufgrund der Fakultät schwierig) und 262144 Iterationen fürln(b)

Michael M.
quelle
Vielleicht sollten Sie weniger Golf spielen, aber mehr Präzision hinzufügen: e(80,5) ->1555962210.2240903- sollte 3276800000 sein
edc65
@ edc65, du hast recht, fixiert für 5 weitere Zeichen.
Michael M.
1
Es ist sehr schön, verschiedene Ansätze zu sehen!
Fehler
3

Golflua 120

Ich nutze die Tatsache, dass

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

und schrieb meine eigenen log& expFunktionen. Die Werte aund bmüssen in Zeilenumbrüchen eingegeben werden, wenn sie im Terminal ausgeführt werden:

\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)))

Probeläufe:

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

Eine ungolfed Lua Version ist,

-- 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)
Kyle Kanos
quelle
Können Sie einige Beispielausgaben bereitstellen?
Fehler
@flawr: Ich schätze, ich kann ... und jetzt fertig
Kyle Kanos