เราควรสอนคำจำกัดความของอัตราการเติบโตเชิงซีมโทติค


35

เมื่อเราทำตามตำรามาตรฐานหรือประเพณีส่วนใหญ่เราจะสอนนิยามของสัญกรณ์โอ๋ใหญ่ในการบรรยายสองสามครั้งแรกของคลาสอัลกอริทึม:

f=O(g) iff (c>0)(n00)(nn0)(f(n)cg(n)).
บางทีเราอาจให้รายการทั้งหมดพร้อมตัวปริมาณทั้งหมด:
  1. f=o(g) iff (c>0)(n00)(nn0)(f(n)cg(n))
  2. f=O(g) iff (c>0)(n00)(nn0)(f(n)cg(n))
  3. f=Θ(g) iff (c>0)(d>0)(n00)(nn0)(dg(n)f(n)cg(n))
  4. f=Ω(g) iff (d>0)(n00)(nn0)(f(n)dg(n))
  5. f=ω(g) iff (d>0)(n00)(nn0)(f(n)dg(n)) )

อย่างไรก็ตามเนื่องจากคำจำกัดความเหล่านี้ไม่ใช่เรื่องง่ายที่จะทำงานด้วยเมื่อมันมาเพื่อพิสูจน์สิ่งที่เรียบง่ายเช่น5nlog4n+nlogn=o(n10/9)ส่วนใหญ่ของเราได้อย่างรวดเร็วย้ายที่จะแนะนำ "เคล็ดลับของขีด จำกัด"

  1. f=o(g)limnf(n)/g(n)0
  2. f=O(g)ถ้ามีอยู่และไม่ใช่ ,limnf(n)/g(n)+
  3. f=Θ(g)ถ้ามีอยู่และเป็นค่ามิได้ ,limnf(n)/g(n)0+
  4. f=Ω(g)ถ้ามีอยู่และไม่ใช่ ,limnf(n)/g(n)0
  5. f=ω(g)ถ้ามีอยู่และเป็น+limnf(n)/g(n)+

คำถามของฉันคือ:

มันจะเป็นความสูญเสียที่ยิ่งใหญ่สำหรับการเรียนการสอนการเรียนระดับปริญญาตรีขั้นตอนวิธีการที่จะใช้เงื่อนไขขีด จำกัด เป็นจำกัดความของ , , ,และ ? นั่นคือสิ่งที่เราทุกคนใช้ท้ายที่สุดและดูเหมือนชัดเจนสำหรับฉันที่การข้ามคำจำกัดความของปริมาณทำให้ชีวิตของทุกคนง่ายขึ้นoOΘΩω

ฉันสนใจที่จะทราบว่าคุณได้พบกรณีธรรมชาติที่น่าเชื่อถือซึ่งจำเป็นต้องใช้มาตรฐานจริงหรือไม่และถ้าไม่คุณมีข้อโต้แย้งที่น่าเชื่อถือที่จะรักษามาตรฐานล่วงหน้าc,n0c,n0


1
แท็กควรเป็น "การสอน" แต่ฉันหาแท็กที่เกี่ยวข้องไม่ได้และฉันไม่ได้รับอนุญาตให้สร้างแท็กใหม่
slimton

1
สิ่งนี้จะดูดซับปริมาณเข้าไปในคำจำกัดความของ epsilon-delta ข้อกังวลเดียวของฉันคือว่านักเรียน CS หลายคนยังไม่ได้ทำการวิเคราะห์ดังนั้นความเข้าใจในข้อ จำกัด จึงเป็นกลไกที่สำคัญ เพื่อให้พวกเขาสามารถคำนวณได้อย่างรวดเร็วแม้ว่าจะไม่ใช่เกมง่ายๆ
ต่อ Vognsen

6
โปรดทราบว่าคำจำกัดความทั้งสองของ O () นั้นไม่เทียบเท่ากัน (ข้อแม้เดียวกันนี้ใช้กับΘ () และΩ ()) พิจารณากรณีที่ f (n) = 2n สำหรับคู่ n และ f (n) = 1 สำหรับคี่ n คือ f (n) = O (n) หรือไม่ ฉันชอบใช้ limsup แทน lim เพื่อที่ฉันจะสามารถพูดได้ว่า f (n) = Θ (n) ในกรณีนี้ (แม้ว่าคำจำกัดความของคุณจะไม่อนุญาตก็ตาม) แต่นี่อาจเป็นความชอบส่วนตัวของฉัน (และแม้แต่การฝึกฝนที่ไม่เป็นมาตรฐาน) และฉันไม่เคยสอนชั้นเรียนเลย
Tsuyoshi Ito

2
@Tsuyoshi: ผมคิดว่าจุด "ขีด จำกัด เคล็ดลับ" ก็คือว่ามันเป็นเงื่อนไขที่เพียงพอ แต่ไม่จำเป็นสำหรับ() (สำหรับก็เป็นสิ่งจำเป็นด้วย) ตัวอย่างการสั่นของฟังก์ชันการสั่นไม่มีขีด จำกัด O()o()
András Salamon

1
คุณไม่ควรแทนที่ symbol byในแต่ละคำจำกัดความและคุณสมบัติ? ฉันพบว่าการใช้รบกวนมากในฐานะนักเรียน ==
Jeremy

คำตอบ:


13

ฉันชอบการสอนคำจำกัดความดั้งเดิมด้วยปริมาณ

IMO โดยทั่วไปมนุษย์มีปัญหาในการทำความเข้าใจกับสูตรและคำจำกัดความที่มีมากกว่าสองทางเลือกของปริมาณโดยตรง การแนะนำปริมาณใหม่สามารถชี้แจงความหมายของคำจำกัดความ ที่นี่สองปริมาณสุดท้ายหมายถึง "สำหรับ n ที่มีขนาดใหญ่พอทั้งหมด" การแนะนำปริมาณชนิดนี้สามารถช่วยได้

รูปภาพที่ฉันวาดเพื่ออธิบายแนวคิดเหล่านี้จับคู่ได้ดีกว่ากับตัวระบุปริมาณ

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

แนวคิดนี้คล้ายกับข้อเสนอแนะที่เราใช้กฎในการคำนวณอนุพันธ์ (ของชื่อพหุนาม, การยกกำลัง, ... , กฎลูกโซ่, ... ) แทนที่คำนิยามของ epsilon-delta ซึ่ง IMHO ไม่ใช่ความคิดที่ดี


ความคิดการปกครองในที่สุดยังเป็นประโยชน์: IFF(n) ตอนนี้ IFF มี ST(x) f(x)g(x)\esitsmn>mf(n)<g(n)fO(g)c>0f(x)cg(x)
Kaveh

9

แก้ไข: การแก้ไขที่สำคัญในการแก้ไข 3

เนื่องจากฉันไม่เคยสอนในชั้นเรียนฉันไม่คิดว่าฉันสามารถเรียกร้องอะไรก็ได้อย่างมั่นใจเกี่ยวกับสิ่งที่เราควรสอน อย่างไรก็ตามนี่คือสิ่งที่ฉันคิดเกี่ยวกับมัน

มีตัวอย่างตามธรรมชาติที่ไม่สามารถใช้“ การ จำกัด เคล็ดลับ” ตามที่เขียนได้ ตัวอย่างเช่นสมมติว่าคุณใช้“ เวกเตอร์ที่มีความยาวผันแปรได้” (เช่นเวกเตอร์ <T> ใน C ++) โดยใช้อาร์เรย์ที่มีความยาวคงที่ที่มีขนาดเป็นสองเท่า (นั่นคือทุกครั้งที่คุณจะมีขนาดเกินกว่าอาร์เรย์ จัดสรรอาเรย์ให้ใหญ่ขึ้นเป็นสองเท่าในขณะนี้และคัดลอกองค์ประกอบทั้งหมด) ขนาดS ( n ) ของอาร์เรย์เมื่อเราเก็บnองค์ประกอบในเวกเตอร์เป็นอำนาจที่เล็กที่สุดของ 2 มากกว่าหรือเท่ากับn เราต้องการที่จะบอกว่าS ( n ) = O ( n ) แต่การใช้ "ขีด จำกัด เคล็ดลับ" ตามที่เขียนไว้เป็นคำจำกัดความไม่อนุญาตให้เราทำเช่นนั้นเพราะS ( n)) / nแกว่งไปแกว่งมาอยู่ในช่วง [1,2) เช่นเดียวกับΩ () และΘ ()

เป็นเรื่องที่ค่อนข้างแยกกันเมื่อเราใช้สัญลักษณ์เหล่านี้เพื่ออธิบายความซับซ้อนของอัลกอริทึมฉันคิดว่าคำจำกัดความของคุณของ) () บางครั้งไม่สะดวก (แม้ว่าฉันเดาว่าคำนิยามนั้นเป็นเรื่องธรรมดา) มันสะดวกกว่าที่จะนิยามว่าf ( n ) = Ω ( g ( n )) ถ้าหาก limsup f ( n ) / g ( n )> 0 นี่เป็นเพราะปัญหาบางอย่างมีความสำคัญน้อยมากสำหรับค่า n () เช่นปัญหาการขึ้นรูปที่สมบูรณ์แบบบนกราฟที่มีจำนวนคี่nของจุดยอด) เช่นเดียวกับΘ () และω ()

ดังนั้นฉันจึงพบว่าคำจำกัดความต่อไปนี้สะดวกที่สุดในการใช้เพื่ออธิบายความซับซ้อนของอัลกอริทึม: สำหรับฟังก์ชันf , g : ℕ→ℕ > 0 ,

  • f ( n ) = o ( g ( n )) ถ้าหากว่า limsup f ( n ) / g ( n ) = 0 (นี่เทียบเท่ากับ lim f ( n ) / g ( n ) = 0)
  • f ( n ) = O ( g ( n )) ถ้าหาก limsup f ( n ) / g ( n ) <∞
  • f ( n ) = Θ ( g ( n )) ถ้าหาก 0 <limsup f ( n ) / g ( n ) <∞
  • f ( n ) = Ω ( g ( n )) ถ้าหาก limsup f ( n ) / g ( n )> 0 (นี่เทียบเท่ากับf ( n ) ไม่ใช่ o ( g ( n ))
  • f ( n ) = ω ( g ( n )) ถ้าหาก limsup f ( n ) / g ( n ) = ∞ (นี่เทียบเท่ากับf ( n ) ไม่ใช่ O ( g ( n )))

หรือเทียบเท่า

  • f ( n ) = o ( g ( n )) ถ้าหากทุกๆc > 0 สำหรับขนาดใหญ่พอn , f ( n ) ≤ cg ( n )
  • f ( n ) = O ( g ( n )) ถ้าหากว่าสำหรับบางc > 0 สำหรับขนาดใหญ่พอn , f ( n ) ≤ cg ( n )
  • f ( n ) = Θ ( g ( n )) ถ้าหากf ( n ) = O ( g ( n )) และf ( n ) = Ω ( g ( n ))
  • f ( n ) = Ω ( g ( n )) ถ้าหากd > 0 สำหรับจำนวนn , f ( n ) ≥ dg ( n )
  • f ( n ) = ω ( g ( n )) ถ้าหากทุกๆd > 0, สำหรับจำนวนn , f ( n ) ≥ dg ( n )

แต่ฉันไม่รู้ว่านี่เป็นเรื่องธรรมดาหรือไม่ นอกจากนี้ฉันไม่ทราบว่าเหมาะสำหรับการสอนหรือไม่ ปัญหาคือบางครั้งเราต้องการนิยามΩ () โดย liminf แทน (เหมือนที่คุณทำในคำจำกัดความแรก) ตัวอย่างเช่นเมื่อเราพูดว่า“ความน่าจะเป็นของความผิดพลาดของอัลกอริทึมแบบสุ่มนี้คือ 2 -Ω ( n ) ” เราไม่ได้หมายถึงว่าน่าจะเป็นข้อผิดพลาดที่ชี้แจงขนาดเล็กเพียงเพื่อเพียบหลาย n !


ฉันยังใช้คำจำกัดความของ limsup แต่สำหรับนักเรียนที่ไม่ได้เห็น limsup (เกือบทั้งหมด) ฉันต้องขยายไปสู่ตัวระบุปริมาณที่ชัดเจนอยู่แล้ว
Jeffε

@JeffE: ฉันยอมรับว่านักเรียนส่วนใหญ่ไม่ได้เห็นการ จำกัด การใช้ดังนั้นถ้าเราใช้การ จำกัด การ จำกัด การสำรองข้อมูลเราจำเป็นต้องใช้ปริมาณแทนในชั้นเรียน
Tsuyoshi Ito

2
ปัญหาของตัวระบุปริมาณเป็นการยากที่จะจดจำและมองเห็นได้ ฉันชอบเพราะสามารถอธิบายได้ว่า "จุด จำกัด สูงสุด" คำอธิบายที่เป็นไปได้คือ: "มันเป็นเหมือนยกเว้นว่าจะทำงานเฉพาะเมื่อลู่ลำดับถ้าลำดับไม่ได้มาบรรจบตัวอย่างเช่นเพราะแกว่งอัลกอริทึมระหว่างรวดเร็วมากสำหรับบางคน.และช้าอื่น ๆแล้วเราจะใช้ จุด จำกัด สูงสุด " l ฉันm l ฉันm n nlimsuplimlimnn
Heinrich Apfelmus

จริงๆแล้วมีตัวอย่างจากธรรมชาติสำหรับอัลกอริธึมที่เวลาทำงานไม่แกว่งหรือไม่?
Heinrich Apfelmus

2
@Heinrich: ฉันได้กล่าวถึงเวลาทำงานของอัลกอริทึมเพื่อค้นหาการจับคู่ที่สมบูรณ์แบบของกราฟบนจุดยอด n แต่มันนับเป็นตัวอย่างที่เป็นธรรมชาติหรือไม่? ฉันได้เพิ่มอีกตัวอย่างหนึ่งซึ่งเวลาทำงานไม่ได้สั่น แต่ f (n) / g (n) ออสซิลเลต ตัวอย่างพูดเกี่ยวกับความซับซ้อนของพื้นที่ แต่ความซับซ้อนของเวลาในตัวอย่างเดียวกันมีคุณสมบัติเดียวกัน
Tsuyoshi Ito

8

การใช้ข้อ จำกัด นั้นค่อนข้างสับสนเนื่องจาก (1) ความคิดที่ซับซ้อนมากขึ้น (2) มันไม่ได้ดึงดูด f = O (g) เป็นอย่างดี (อย่างที่เราเห็นในการสนทนาข้างต้น) ฉันมักจะพูดคุยเกี่ยวกับฟังก์ชั่นจากตัวเลข Natural (บวกอย่างเคร่งครัด) ไปยังหมายเลข Natural (ซึ่งพอเพียงสำหรับเวลาทำงาน) ข้ามสิ่งเล็ก ๆ น้อย ๆ จากนั้นคำจำกัดความที่กระชับและเหมาะสมสำหรับนักศึกษาชั้นปีที่ 1:

Dfn: f = O (g) ถ้าสำหรับ C สำหรับ n ทั้งหมดเรามี f (n) <= C * g (n)


1
ครั้งแรกที่ฉันไม่ชอบคำจำกัดความนี้เพราะระบุ "all n" ปิดบังความจริงที่สำคัญว่าสัญกรณ์ O () เพียงแค่ใส่ใจเกี่ยวกับพฤติกรรมของฟังก์ชั่นสำหรับ n ขนาดใหญ่ อย่างไรก็ตามไม่ว่าเราจะเลือกคำจำกัดความใดฉันเดาว่าเราควรอธิบายข้อเท็จจริงนี้พร้อมคำจำกัดความ เมื่อคิดอย่างนั้นการระบุคำจำกัดความง่ายๆนี้ก็ค่อนข้างดี
Tsuyoshi Ito

ในขณะที่สิ่งนี้จับสาระสำคัญฉันไม่ชอบว่าถ้าสำหรับทุก ,สำหรับทั้งหมดจนถึงและมิฉะนั้นแต่คำจำกัดความนี้ล้มเหลวในการจับความสัมพันธ์นี้ ดังนั้นเราจึงต้องเพิ่ม handwaving บางอย่างเกี่ยวกับฟังก์ชั่นที่มีความประพฤติดีในบางแง่มุม n g ( n ) = 0 n N 0 g ( n ) = f ( n ) + 1 f = O ( g )f(n)=nng(n)=0nN0g(n)=f(n)+1f=O(g)
András Salamon

2
ประเด็นของการพูดคุยเกี่ยวกับฟังก์ชั่นที่มีช่วงคือตัวเลขธรรมชาติ (ไม่รวม 0) นั้นแน่นอนว่าจะไม่เกิดปัญหากับ g (n) = 0
โนม

1
@Warren Victor Shoup ในหนังสือของเขาเกี่ยวกับComputational Number Theoryใช้สัญกรณ์แทนในการวิเคราะห์เวลาทำงานซึ่งฉันพบว่าเรียบร้อย บันทึกalen(a)loga
Srivatsan Narayanan

1
@Warren (ต่อ) นี่คือวิธีการที่เขาอธิบายว่า "ในการแสดงเวลาการทำงานของอัลกอริทึมในแง่ของการป้อนข้อมูลเรามักชอบที่จะเขียนมากกว่าเหตุผลหนึ่งก็คือความงาม:. เขียนเน้นความจริงที่ว่าเวลาทำงานเป็นหน้าที่ของความยาวบิตของเหตุผลอีกอย่างคือทางเทคนิค: สำหรับการประมาณการขนาดใหญ่เกี่ยวข้องกับฟังก์ชั่นในโดเมนใด ๆ ความไม่เท่าเทียมกันที่เหมาะสมควรเก็บไว้ตลอดทั้งโดเมน มันไม่สะดวกในการใช้ฟังก์ชั่นเช่นซึ่งหายไปหรือไม่ได้รับการตอบรับจากอินพุตบางอย่าง " alen(a)logalen(a)aOlog
Srivatsan Narayanan

5

เมื่อฉันเรียนหลักสูตรพื้นฐานเราได้รับเป็นคำจำกัดความและอีกสิ่งหนึ่งเป็นทฤษฎีบทc,n0

ฉันคิดว่าอันแรกนั้นเป็นเรื่องธรรมชาติสำหรับคนจำนวนมากที่คิดว่าไม่ต่อเนื่องมากกว่าต่อเนื่องนั่นคือนักวิทยาศาสตร์คอมพิวเตอร์ส่วนใหญ่ (จากประสบการณ์ของฉัน) นอกจากนี้ยังเหมาะกับวิธีที่เรามักจะพูดถึงสิ่งเหล่านั้นดีกว่า: "มีฟังก์ชันพหุนามระดับ 3 ซึ่งเป็นขอบเขตบนสำหรับนี้จนถึงปัจจัยคงที่"f

แก้ไข : คุณสามารถใกล้ชิดกับวิธีพูดนี้มากขึ้นถ้าคุณใช้คำจำกัดความนี้: (โปรดทราบว่าเชื่อมต่อคำจำกัดความนี้กับคำที่ได้รับตามปกติ)fO(g):⇔c,d>0n0:f(n)cg(n)+dd=f(n0)

สิ่ง จำกัด มีประโยชน์มากสำหรับการคำนวณคลาสความซับซ้อนนั่นคือด้วยปากกาและกระดาษ

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


4

หลังจากศึกษาแนวคิดเหล่านี้เมื่อไม่กี่ปีที่ผ่านมาพวกเขาไม่ใช่คนที่ยากที่สุดที่จะเข้าใจในชั้นเรียนของฉัน (ตรงกันข้ามกับแนวคิดเช่นอุปนัย ขีด จำกัด และ limsups เป็นเพียง "ใช้งานง่าย" สำหรับผู้ที่คุ้นเคยกับแคลคูลัสในความคิดของฉัน แต่นักเรียนที่มีพื้นฐานทางคณิตศาสตร์ดังกล่าวจะมีภูมิหลังทางทฤษฎีที่กำหนดอยู่แล้วเพื่อให้พวกเขาสามารถดำเนินการคัดเลือกที่ไม่ต่อเนื่อง

และที่สำคัญยิ่งกว่านั้นโปรดจำไว้ว่าในที่สุดนักเรียนของคุณจะไป (หวังว่า) เพื่ออ่านหนังสือเรียนทฤษฎี cs อื่น ๆ และบางทีอาจค้นคว้าเอกสารในวันหนึ่ง เช่นนี้จะเป็นการดีกว่าสำหรับพวกเขาที่จะรู้สึกสะดวกสบายกับสัญกรณ์มาตรฐานในทุ่งหญ้า ไม่มีอันตรายใด ๆ ที่จะให้คำจำกัดความทางเลือกแก่พวกเขาเช่นกันเมื่อพวกเขาได้หลอมรวมมาตรฐาน


3

สำหรับการใช้เวลาที่น่าสนใจเกี่ยวกับเรื่องนี้ดูที่ดอน Knuth เขียนจดหมายอย่าง"แคลคูลัสผ่าน O สัญกรณ์" เขาสนับสนุนมุมมองย้อนกลับว่าควรสอนแคลคูลัสผ่านเครื่องหมาย 'A', 'O' และ 'o'

หมายเหตุ: เขาใช้สัญกรณ์ "A" เป็นขั้นตอนเบื้องต้นในการกำหนดสัญกรณ์ "O" มาตรฐาน ปริมาณคือของ (เช่น ) ถ้า . โดยเฉพาะอย่างยิ่งจะทำให้ความรู้สึกที่จะบอกว่าคือ(200)A y x = A ( y ) | x | y 100 A ( 200 )xAyx=A(y)|x|y100A(200)


1
  1. คำจำกัดความของ Tsuyoshi Ito ดูไม่ถูกต้องนัก สำหรับโอเมก้าน้อยและโอเมก้าใหญ่คำจำกัดความควรใช้ liminf ไม่ใช่ limsup คำจำกัดความของ big-theta ต้องการทั้งขอบเขตที่ต่ำกว่าบน liminf และขอบเขตบนของ limsup

  2. คำจำกัดความหนึ่งของ f (n) = O (g (n)) คือมีฟังก์ชันอื่น f '(n)> = f (n) เช่นนั้น Lim f' (n) / g (n) <อนันต์

  3. ทำไมมือใหม่ถึงได้รับอนุญาตให้โพสต์คำตอบ แต่ไม่แสดงความคิดเห็น?


1
สำหรับข้อ 1 ฉันหมายถึงการ จำกัด การใช้งานในทุกกรณีและเหตุผลได้อธิบายไว้ในวรรคที่สองของคำตอบของฉัน
Tsuyoshi Ito

มันเป็นกลไกการบล็อกสแปมอย่างน่าเสียดาย
Suresh Venkat

นอกจากนี้คุณสามารถใช้ลาเท็กซ์ในคำตอบของคุณ
Suresh Venkat

1

ก่อนอื่นฉันพยายามพัฒนาให้นักเรียนมีสัญชาตญาณก่อนแสดงสมการ

  • "ผสานเรียงลำดับเทียบกับแทรกเรียงลำดับ" เป็นจุดเริ่มต้นที่ดี

จากนั้นต่อมาฉันพยายามแสดงทั้งสองวิธี นักเรียนที่อาศัยสัญชาตญาณมากกว่าชอบ ในขณะที่คนที่ต้องพึ่งพาคณิตศาสตร์ equasions พีชคณิต ฯลฯ พวกเขาชอบ " " คำจำกัดความลิมn

f=O(g) iff (c>0)(n00)(nn0)(f(n)cg(n)).
limn

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

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