แก้ปัญหาสัญกรณ์คณิตศาสตร์


14

ลองนึกภาพฉันมีปัญหาการบ้านจำนวนอนันต์ (!) แต่ละข้อให้เป็นจำนวนเต็ม

สัญลักษณ์ปัญหาคณิตศาสตร์เป็นสัญลักษณ์สำหรับอธิบายชุดย่อยของปัญหาโดยใช้ตัวระบุปัญหา

นิพจน์ MPN อาจประกอบด้วยหลายสิ่ง:

  • ค่าเดียว 99 -> {99}นี้แสดงให้เห็นชุดที่มีจำนวน:
  • ช่วงที่เรียบง่าย 10~13 -> {10, 11, 12, 13}นี้แสดงให้เห็นชุดที่มีตัวเลขทั้งหมดจากจุดเริ่มต้นถึงจุดสิ้นสุดของช่วง: หากด้านซ้ายหรือด้านขวาหายไปแสดงว่าเป็นอินฟินิตี้หรืออินฟินิตี้ตามลำดับ: ~10 -> {x|x ≤ 10}; ~ -> ℤ.
  • นิพจน์ MPN ตามด้วย "skip" และนิพจน์ MPN อื่น 10~20 skip 12~14 -> {10, 11, 15, 16, 17, 18, 19, 20}แสดงให้เห็นถึงความแตกต่างนี้ของทั้งสองชุด:
  • นิพจน์ MPN สองนิพจน์คั่นด้วยเครื่องหมายจุลภาค สิ่งนี้แสดงถึงการรวมกันของสองชุด: 1,8~9,15~17 -> {1,8,9,15,16,17}.

ผู้ประกอบการ "ข้าม" ผูกแน่นกว่าตัวดำเนินการคอมมาดังนั้น16,110~112 skip 16 -> {16,110,111,112}(16 ไม่รวมอยู่ในชุด{110,111,112}ดังนั้นการยกเว้น 16 จึงไม่สำคัญ)

คุณยังสามารถใส่นิพจน์ในวงเล็บเพื่อแก้ความกำกวม: 1~9 skip (2~8 skip (3~7 skip (4~6 skip 5))) -> {1,3,5,7,9}

นี่คือไวยากรณ์:

<expr>  ::= "(" <expr> ")"
         || <number>
         || [<number>] "~" [<number>]
         || <expr> "skip" <expr>
         || <expr> "," <expr>

งานของคุณคือการเขียนโปรแกรมที่รับสองอินพุต:

  • นิพจน์ MPN
  • หมายเลข

และส่งออกค่าความจริงหรือเท็จบางอย่างขึ้นอยู่กับว่าปัญหานั้นอยู่ในชุดที่อธิบายโดยการแสดงออกของ MPN

ข้อมูลจำเพาะ

  • คุณสามารถสันนิษฐานได้ว่าอินพุตแรกเป็นนิพจน์ MPN ที่มีรูปแบบที่ดี (เช่นตรงกับไวยากรณ์ด้านบน)
  • ตัวเลขในนิพจน์ MPN เป็นจำนวนเต็มเสมอ พวกมันอาจเป็นลบหรือศูนย์ แต่จะไม่มีส่วนที่เป็นเศษส่วน
  • นี่คือดังนั้นการส่งที่ถูกต้องสั้นที่สุด (วัดเป็นไบต์) ชนะ
  • คุณสามารถใช้อักขระที่แตกต่างกันสำหรับ~และ,ถ้าคุณต้องการ

กรณีทดสอบ

10~20             14 -> True
10~20             20 -> True
10~20 skip 14~18  17 -> False
~ skip 6          8  -> True
16,17 skip 16     16 -> True
(16,17) skip 16   16 -> False
~10,5~            8  -> True
~10,5~            4  -> True
6 skip 6,~        6  -> True

เป็นไปได้ไหมที่จะใช้ตัวอักษรอื่นเพื่อแทนโอเปอเรเตอร์ตัวอย่างเช่นการใช้ # แทน ~
rahnema1

1
@ rahnema1 สำหรับ~และแต่ไม่ได้สำหรับ, skip
ผลไม้แยกแยะ

3
ทำไม ~ 10,5 ~ เป็นเท็จสำหรับ 4 เพราะนั่นคือการรวมกันของ - อินฟินิตี้ถึง 10 และ 5 ถึงอินฟินิตี้สิ่งแรกที่รวม 4
ev3commander

@ ev3commander แก้ไขแล้ว ฉันมักจะเลอะกรณีทดสอบของฉัน ฉันพนันได้ว่าการท้าทายของฉันจะชัดเจนขึ้นถ้าฉันไม่ได้เพิ่ม: P
ผลไม้ที่แยกแยะได้

1
@ Challenger5 ฉันได้เพิ่มกรณีทดสอบ6 skip 6,~ซึ่งฉันเชื่อว่าฉันตีความถูกต้อง อีก 2 คำตอบยังไม่เป็นที่พอใจ (อีกครั้งสมมติว่าฉันตีความถูกต้อง) หากฉันเข้าใจผิดโปรดแก้ไขให้ถูกต้องและให้ความกระจ่าง แต่จากความเข้าใจของฉันมันควรจะตรงกับทุกอย่าง นี่คือกรณีต่างๆที่ฉันพูดถึงก่อนหน้านี้ซึ่งฉันคิดว่าสามารถช่วยได้มากเมื่อทำการทดสอบโซลูชันของเรา
ต้มตุ๋น

คำตอบ:


3

PowerShell , 189 195 ไบต์

param($m,$q)('$m',"'(\d*)~(\d*)','($q-ge(`$1)-and$q-le(`$2))'","'\(\)',$q","'((?<=,|skip )\d+|\d+(?=,| skip))','($q-eq`$1)'","'skip','-and!'"-join'-replace'|iex|% Sp* ','|%{"($_)"})-join'-or'|iex

คำอธิบาย

ฉันรู้ตั้งแต่แรกแล้วว่าอินฟินิตี้ทำให้ไม่สามารถสร้างอาร์เรย์และทดสอบค่าได้

ฉันดูเป็นช่วง แต่ใน. Net พวกเขาไม่มีช่วงที่ต้องการ ( ความยาวของช่วงถูก จำกัด จำนวนเต็ม (32 บิต) ที่ลงนามดังนั้นแม้ว่ามันจะโอเคที่จะ จำกัด ช่วงให้เป็น 32- บิต int ฉันจะไม่สามารถรับมือกับทุกช่วงได้

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

โดยทั่วไปแล้วฉันทำลายมันลงเป็นกฎไม่กี่:

  • ช่วงการทำงานกับครั้งแรกนั้นง่ายกว่าเพราะพวกเขาไม่สามารถแสดงออกได้ทั้งสองด้าน หลักฐานเป็น2~8เหมือนการพูดn >=2 && n <=8แต่เมื่อปลาย&&ด้านใดด้านหนึ่งหายไปให้ออกไปและด้านที่หายไป เมื่อทั้งสองจะหายไปผมจะไป $trueแต่เดิมเพียงแทนที่ด้วย สิ่งที่ฉันทำลงไปไม่ได้ทดสอบจริง ๆ สำหรับด้านที่ขาดหายไป แต่ฉันแน่ใจว่าได้ใส่หมายเลข()ไว้
  • จากนั้นทำการทดแทนตรงที่แทนที่วงเล็บว่าง()ด้วยค่าอินพุต ดังนั้นในกรณีของ MPN ที่ชอบ~8ด้วยค่าอินพุตของ55การแทนที่แรกจะสร้างขึ้น(55-ge()-and55-le(8))จากนั้นการแทนที่ครั้งที่สองจะมาพร้อมที่จะทำให้มันกลาย(55-ge55-and55-le(8))เป็นโมฆะส่วนหนึ่งของช่วงนั้น
  • ต่อไปฉันต้องจัดการกับตัวเลขแต่ละตัวใน MPN แต่ต้องระวังที่จะไม่ยุ่งกับตัวเลขที่ฉันแทรกไว้ก่อนหน้านี้ มันเป็นเพียงตัวเลขใน,รายการคั่นด้วยเครื่องหมายจุลภาคและตัวเลขแต่ละรายการก่อนหรือหลัง a skipดังนั้นฉันจึงใช้การตรวจสอบระยะยาวที่น่าเสียดาย
  • skipเป็นพื้นเดียวกันเป็น-and -notดังนั้นผมจึงแทนที่ตรงskipไป-and!(โดยใช้!เป็นชวเลข-not)
  • สิ่งที่ยุ่งยากต่อไปคือลำดับความน่าเชื่อถือต่ำสำหรับเครื่องหมายจุลภาคที่เหลือ ฉันเดิมเพียงแทนที่ด้วย-orแต่มันไม่ได้บัญชีสำหรับการแสดงออกต่อมาจึงได้รับการสร้างรหัสเช่น16,17 skip 16 ($n-eq16)-or($n-eq17) -and! ($n-eq16)จำเป็นต้องใช้วงเล็บ แต่ดูเหมือนไม่สามารถใช้งานได้ด้วยการแทนที่แบบตรง เนื่องจากทุกสิ่งอื่น ๆ -orถูกแทนที่ยกเว้นเครื่องหมายจุลภาคและพวกเขามีความสำคัญต่ำสุดที่ฉันเพิ่งแยกทั้งสร้างสตริงในเครื่องหมายจุลภาคที่เหลือแล้วห่อองค์ประกอบในวงเล็บในแต่ละครั้งและไปสมทบกับ

ในที่สุดรหัสที่สร้างขึ้นจะถูกส่งไปยังInvoke-Expression( iex) เพื่อดำเนินการและจากนั้นเราจะได้รับผลบูลีน ( คุณสามารถดูรหัสที่สร้างขึ้นแทนผลลัพธ์ที่นี่ )

สิ่งนี้ใช้เวลานานเกินไปและฉันแน่ใจว่ามีห้องพักที่จะบีบอีกไม่กี่ไบต์ แต่ฉันไม่สามารถดูได้อีก :-p


2

Perl, 99 130 ไบต์

sub f{($_,$n)=@_;s/(-?\d+)?~(-?\d+)?|(-?\d+)/!(defined$3?$n!=$3:length$1&&$1>$n||length$2&&$n>$2)+0/ge;s/skip/&&!/g;s/,/||/g;eval}

ลองใช้กับ Ideone

Ungolfed:

sub f {
    my ($e, $n) = @_;

    $e =~ s/(-?\d+)?~(-?\d+)?|(-?\d+)/ (defined($3) ? $n == $3 : (!length($1) || $n >= $1) && (!length($2) || $n <= $2)) + 0 /ge;
    $e =~ s/skip/ && ! /g;
    $e =~ s/,/ || /g;

    return eval($e);
}

1
ล้มเหลวสำหรับ ~ -2 สำหรับอินพุต -2 นอกจากนี้เมื่อใส่ -? ก่อนทั้งสาม \ d *
Kjetil S.

@KjetilS แก้ไขตัวเลขติดลบและศูนย์
เดนิส Ibaev

รหัสที่ดี (การแยกไวยากรณ์แบบเต็มเป่ามักไม่จำเป็น regexes ง่ายกว่า)
Kjetil S.

1

JavaScript (ES6), 221 292 287 309 274 277 278 ไบต์

(-5 ไบต์ขอบคุณ Okx)

(j,v,m=1/0,Z=/(skip)([^,]+)/g)=>eval(j[M='replace'](/(-?\d*)~(-?\d*)/g,(e,a,b)=>(a[M]('-','#')||-m)+'<='+(T=v[M]('-','#'))+'&&'+T+'<='+(b[M]('-','#')||m))[M](Z,i=o=>o.match(Z)?i(o[M](Z,'&&!($2)')):o)[M](/,/g,'||')[M](/(^|[^=&#\d])(\d+)([^<\d]|$)/g,'$1$2=='+v+'$3')[M](/#/g,'-'))

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

ตัวอย่างการทดสอบ

D=(j,v,m=1/0,Z=/(skip)([^,]+)/g)=>eval(j[M='replace'](/(-?\d*)~(-?\d*)/g,(e,a,b)=>(a[M]('-','#')||-m)+'<='+(T=v[M]('-','#'))+'&&'+T+'<='+(b[M]('-','#')||m))[M](Z,i=o=>o.match(Z)?i(o[M](Z,'&&!($2)')):o)[M](/,/g,'||')[M](/(^|[^=&#\d])(\d+)([^<\d]|$)/g,'$1$2=='+v+'$3')[M](/#/g,'-'))
MPN Expression:<input type="text" value="1~9 skip (2~8 skip (3~7 skip (4~6 skip 5)))" id="MPN"></input>
<br>
Integer:<input type="number" id="INT" value=6></input>
<input type="button" value="Submit" onclick="T=r=>document.getElementById(r).value;console.log(D(T('MPN'),T('INT')))"></input>


@AriaAx มันควรจะทำงานตอนนี้
R. Kap

@ R.Kap คุณสามารถใช้ สำหรับ1/0 Infinity
Okx

@ R.Kap ฉันลองตาม6ด้วยนิพจน์6 skip 6,~ที่ฉันเชื่อว่าควรจะเป็นtrueแต่มันกลับfalseมา
ต้มตุ๋น

@briantist อันที่จริงผมเชื่อว่าควรจะกลับมาfalseเป็นskipนำไปใช้กับทุกอย่างที่ตามมา ( 6,~ในกรณีนี้) ตราบใดที่มันจะไม่ห่อภายในวงเล็บ ดังนั้นผมเชื่อว่ามันควรจะกลับtrueได้ที่(6 skip 6),~มากกว่าด้วยการป้อนข้อมูลจำนวนเต็ม6 skip 6,~ 6
R. Kap

@briantist ในคำอื่น ๆ6 skip 6,~ควรจะตรงกับอะไรที่มันแสดงให้เห็นถึงความแตกต่างระหว่างชุดและชุด{6} {6,-Infinity...Infinity}
R. Kap

0

Röda + bc, 183 ไบต์

f x{{["x=",x,"\n"];replace" ","",",","||","skip\\(","&&!","skip([0-9~]+)","&&!($1)","(?<!~|\\d)(\\d+)(?!~|\\d)","x==$1","(\\d*)~(\\d*)","x>=($1)&&x<=($2)","\\(\\)",x;["\n"]}|exec"bc"}

นี่คล้ายกับคำตอบของ PowerShell (หรือฉันคิดว่าฉันไม่เข้าใจ PowerShell) มันต้องใช้ตัวเลขเป็นอาร์กิวเมนต์และรหัสเป็นค่าในอินพุตสตรีมเช่นนี้main { push("1~9") | f(5) }มันต้องใช้ตัวเลขเป็นข้อโต้แย้งและรหัสที่เป็นค่าในการสตรีมใส่เช่นนี้

ฉันคิดว่ามันใช้งานได้อย่างน้อยก็แก้กรณีทดสอบทั้งหมด สคริปต์ด้านล่างนี้สามารถใช้ตรวจสอบสิ่งนี้ได้

main {
    readLines("/tmp/tests.txt") | split(sep=";") | for code, num, ans do
        push(code, " -> ")
        print(code) | replace" ","",",","||","skip\\(","&&!","skip([0-9~]+)","&&!($1)","(?<!~|\\d)(\\d+)(?!~|\\d)","x==$1","(\\d*)~(\\d*)","x>=($1)&&x<=($2)","\\(\\)",num
        a := push(code) | f(num) | head()
        result := push("OK") if [ (a = "0" and ans = "False") or (a = "1" and ans = "True") ] else push("FAIL")
        print code, " ; ", num, " -> ", a, " ", ans, " (", result, ")\n"
    done
}

และการทดสอบ:

10~20;14;True
10~20;20;True
10~20 skip 14~18;17;False
~ skip 6;8;True
16,17 skip 16;16;True
(16,17) skip 16;16;False
~10,5~;8;True
~10,5~;4;True
6 skip 6,~;6;True
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.