ยกระดับพลัง


12

ท้าทาย

ความท้าทายคือการเขียนโปรแกรมที่ใช้เวลาเป็นบวกตัวเลขaและไม่ใช่ศูนย์จำนวนbและเอาท์พุทa^b(ยกกำลังข) คุณสามารถใช้+ - * / abs()เป็นฟังก์ชัน / ตัวดำเนินการทางคณิตศาสตร์เท่านั้น สิ่งเหล่านี้สามารถใช้ได้กับค่าสเกลาร์เท่านั้น แต่ไม่สามารถใช้ได้กับรายการหรืออาร์เรย์ทั้งหมด

ตัวอย่าง:

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

ที่เกี่ยวข้อง: http://xkcd.com/217/

รายละเอียด

คุณสามารถเขียนฟังก์ชั่นหรือโครงสร้างที่คล้ายกันเพื่อใช้ในคอนโซล หากคุณไม่สามารถใช้อินพุตคอนโซลคุณสามารถสมมติว่าทั้งสองหมายเลขถูกบันทึกในตัวแปรและ ouptut ผ่านเอาต์พุตมาตรฐานหรือเขียนไปยังไฟล์ ผลลัพธ์จะต้องมีความถูกต้องอย่างน้อย 4 หลักนัยสำคัญ คุณสามารถสันนิษฐานได้ว่าทั้งคู่aและbไม่ใช่ศูนย์ ไม่สามารถรันไทม์ที่มากกว่า 1 นาทีได้อย่างมีนัยสำคัญ จำนวนไบต์น้อยที่สุดจะเป็นผู้ชนะ โปรดอธิบายโปรแกรมและอัลกอริทึมของคุณ

แก้ไข: ต้องพิจารณาเฉพาะฐานบวก คุณสามารถสันนิษฐานa>0ได้ ระวังว่าตัวเลขทั้งสองไม่จำเป็นต้องเป็นจำนวนเต็ม !!!


3
คุณขอให้เรายกกำลังทศนิยม? ชอบพูดว่า 4.5 ^ 4.5?
fuandon

1
นี่หมายความว่าเราต้องส่งออกจำนวนจินตภาพด้วยหรือไม่ถ้าฐานเป็นลบ?
bebe

1
สิ่งที่ควร-0.5 ** 0.5จะเป็น
เดนนิส

ตกลงฉันไม่คิดว่ากรณีนี้ขอบคุณ: ฐานเชิงลบจะต้องดำเนินการไม่ถูกต้อง @fuandon ว่าตัวเลขจริงสามารถมีทศนิยม (อย่างน้อยที่สุดในการเขียนโปรแกรมภาษา =)
flawr

ฉันต้องการเพิ่มกรณีทดสอบด้วย b <0: `4.5 ^ -4.5 = 0.0011496 '
edc65

คำตอบ:


3

Python, 77

เช่นเดียวกับคำตอบอื่น ๆ สิ่งนี้ขึ้นอยู่กับบันทึกและประสบการณ์ แต่ฟังก์ชั่นคำนวณโดยการแก้สมการเชิงอนุพันธ์สามัญเชิงตัวเลข

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

มันตอบสนองความต้องการหรือไม่ สำหรับตัวอย่างในคำถามใช่ สำหรับ a ขนาดใหญ่มันจะใช้เวลานานมาก สำหรับ a หรือ b ขนาดใหญ่มันจะไม่ถูกต้อง

ตัวอย่าง:

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

ปรับปรุง: ข้อบกพร่องขอรายละเอียดเพิ่มเติมเกี่ยวกับคณิตศาสตร์ดังนั้นที่นี่คุณไป ฉันพิจารณาปัญหาค่าเริ่มต้นต่อไปนี้:

  • x '(t) = x (t), กับ x (0) = 1 การแก้ปัญหาคือ exp (t)
  • y '(t) = โดย (t) โดยที่ y (0) = 1 การแก้ปัญหาคือ exp (bt)

หากฉันสามารถหาค่าของ t เช่นนั้น x (t) = a แล้วฉันจะมี y (t) = exp (bt) = a ^ b วิธีที่ง่ายที่สุดในการตัวเลขการแก้ปัญหาค่าเริ่มต้นเป็นวิธีการออยเลอร์ คุณคำนวณอนุพันธ์ของฟังก์ชั่นที่ควรจะมีจากนั้นทำตามขั้นตอนไปในทิศทางของอนุพันธ์และสัดส่วนกับมัน แต่ปรับสัดส่วนด้วยค่าคงที่เล็ก ๆ นั่นคือสิ่งที่ฉันทำใช้ขั้นตอนเล็ก ๆ จนกระทั่ง x ใหญ่เท่ากับ a แล้วดูว่า y คืออะไรในเวลานั้น นั่นคือวิธีที่ฉันคิด ในรหัสของฉัน t ไม่เคยคำนวณอย่างชัดเจน (มันคือ 1e-7 * จำนวนขั้นตอนของลูป while) และฉันบันทึกอักขระบางตัวโดยทำการคำนวณสำหรับ x ด้วย a แทน


ดูดีมากฉันดีใจที่ได้เห็นวิธีการอื่นที่แตกต่างกัน! คุณช่วยเล่าเพิ่มเติมเกี่ยวกับสมการอนุพันธ์เหล่านี้ให้เราฟังได้ไหม โดยทั่วไปฉันรู้ว่ามันคืออะไร แต่ฉันไม่สามารถหาวิธีที่โปรแกรมของคุณใช้พวกเขา =)
ข้อผิดพลาด

@flawr: ตกลงฉันอัปเดตพร้อมรายละเอียดเพิ่มเติมเกี่ยวกับคณิตศาสตร์
reheated

6

JavaScript (E6) 155 174 191

แก้ไข 2ตามที่แนะนำโดย @bebe การใช้ฟังก์ชั่นวนซ้ำ (ทำงานแย่ลง แต่สั้นลง)
ฟังก์ชั่น R ที่เปลี่ยนไปเล็กน้อยเพื่อหลีกเลี่ยง
การเพิ่มชุดทดสอบ'เรียกซ้ำมากเกินไป' ฟังก์ชั่นนี้ทำงานได้ดีสำหรับฐาน <3000 และเลขชี้กำลังในช่วง -50 .. 50
แก้ไข Golfed มากขึ้นและแม่นยำยิ่งขึ้น

จำนวนจริงใด ๆ สามารถประมาณด้วยจำนวนตรรกยะ (และจำนวนจริง 'จริง' เก็บตัวเลขตามมาตรฐานจริง ๆ ) จำนวนตรรกยะสามารถแสดงเป็นเศษส่วน a / b ด้วยจำนวนเต็มและ a x ^ (a / b) คือรูต b ของ (x ^ a) หรือ (รูท b ของ x) ^ a การยกกำลังจำนวนเต็มค่อนข้างง่ายโดยการยกกำลังสอง จำนวนเต็มรูตสามารถประมาณโดยใช้วิธีตัวเลข

รหัส

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

ทดสอบในคอนโซล FireFox หรือ 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))
}

ทำได้ดีไม่แม่นยำมากนัก แต่มันเป็นอัลกอริธึมที่ดี =)
ข้อผิดพลาด

คุณสามารถอธิบายสิ่งนี้e&1&&(r*=b)ทำอะไรยกเว้นการคูณrด้วยb?
ข้อบกพร่อง

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

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

1
นี่คือรหัสการทำงาน: 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))(ฉันต้องเหนื่อย)
bebe

6

Haskell, 85 90

อัลกอริทึมบันทึกการใช้งานมาตรฐาน ขณะนี้มีชื่ออื่นให้โกนอักขระอีกสองสามตัว:

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]

raiseตอนนี้เรียกว่า(%)หรือ%ในสัญกรณ์มัดแม้กระทั่งการใช้มันใช้ไบต์น้อยลง:4.5%(-4.5)

รุ่นที่ยังไม่ได้แก้ไขก็ใช้เพียง 172 ไบต์:

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 ไบต์

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)

ตัวอย่าง :

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

ใช้ชุดเทย์เลอร์
b^x = 1 + ln(b)*x/1! + (ln(b)*x)^2/2! + (ln(b)*x)^3/3! + (ln(b)*x)^4/4! + ...
ด้วยการประมาณลอการิทึมธรรมชาติ :
ln(b) = (1-1/x) + (1-1/x)^2/2 + (1-1/x)^3/3 + (1-1/x)^4/4 + ...

ฉันใช้การคำนวณ 128 ซ้ำเพื่อคำนวณb^x(การวนซ้ำมากขึ้นเป็นเรื่องยากเนื่องจากแฟคทอเรียล) และ 262144 การทำซ้ำสำหรับln(b)


บางทีคุณควรตีกอล์ฟน้อยลง แต่เพิ่มความแม่นยำมากขึ้น: e(80,5) ->1555962210.2240903- ควรเป็น 3276800000
edc65

@ edc65 คุณพูดถูกแก้ไขเพื่อรับอีก 5 ตัวอักษร
Michael M.

1
เป็นเรื่องที่ดีมากที่ได้เห็นแนวทางที่แตกต่าง!
ข้อผิดพลาด

3

golflua 120

ฉันใช้ความจริงที่

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

และเขียนlog& expฟังก์ชั่นของฉันเอง ค่าaและbจำเป็นต้องป้อนในการขึ้นบรรทัดใหม่เมื่อรันในเทอร์มินัล:

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

ตัวอย่างการวิ่ง:

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

รุ่น 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)

คุณสามารถให้ผลลัพธ์ตัวอย่างบางส่วนได้หรือไม่?
ข้อผิดพลาด

@flawr: ฉันคิดว่าฉันสามารถ ... และตอนนี้เสร็จแล้ว
Kyle Kanos
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.