ขั้นตอนวิธีการวาดเส้นที่รวดเร็ว


9

งานคือการหาวิธีการวาดเส้นแนวนอนในอาร์เรย์ของจำนวนเต็ม 16 บิต

เราสมมติว่าอาร์เรย์พิกเซล 256x192 มี 16 พิกเซลต่อคำ เส้นคือการทำงานที่ต่อเนื่องกันของบิต (1) ชุด บรรทัดสามารถเริ่มต้นตรงกลางของคำใด ๆ ทับซ้อนคำอื่น ๆ และสิ้นสุดในคำใด ๆ พวกเขาอาจเริ่มต้นและสิ้นสุดในคำเดียวกัน พวกเขาอาจไม่ห่อบนบรรทัดถัดไป คำแนะนำ: คำกลางนั้นง่าย - เพียงแค่เขียน 0xffff แต่ขอบจะมีเล่ห์เหลี่ยมเช่นเดียวกับการจัดการเคสสำหรับการเริ่มต้นและสิ้นสุดในคำเดียวกัน ฟังก์ชัน / โพรซีเดอร์ / รูทีนต้องใช้พิกัด x0 และ x1 เพื่อระบุจุดเริ่มต้นและจุดหยุดในแนวนอนรวมถึงพิกัด ay

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


2
Codegolf เป็นโค้ดสั้น ๆ ไม่ใช่รหัสเร็วหรือปรับให้เหมาะกับความเร็ว
hallvabo

@ hallvabo โซลูชันของฉันค่อนข้างสั้นประมาณ 5 บรรทัดเมื่อตรวจสอบขอบเขตและคุณสมบัติเพิ่มเติม (เช่นสลับพิกเซลแทนที่จะตั้งค่า)
โทมัสโอ

9
@hallvabo, ไซต์นี้ไม่เพียง codegolf ยังเกี่ยวกับการปรับให้เหมาะสมสำหรับความเร็ว แต่ไม่ใช่การเพิ่มประสิทธิภาพทุกประเภท: ไม่ใช่รายละเอียดฮาร์ดแวร์ แต่ความซับซ้อนของอัลกอริทึม
Nakilon

@Nakilon: ฉันไม่เห็นด้วย แล้วทำไมไซต์นี้ถึงมีชื่อว่า Code Golf? มีไซต์อื่นอีกหลายพันแห่งสำหรับการอภิปรายที่ซับซ้อนของอัลกอริทึมและการเพิ่มประสิทธิภาพความเร็ว
hallvabo

5
@ hallvabo: จากคำถามที่พบบ่อย - "Code Golf - Stack Exchange สำหรับนักกอล์ฟรหัสและสำหรับผู้ที่สนใจในการเล่นกอล์ฟรหัส (ตั้งแต่เริ่มต้นจนถึงผู้เชี่ยวชาญ) และปริศนาการเขียนโปรแกรม" ฉันคิดว่านี่เป็นปริศนาการเขียนโปรแกรม
Thomas O

คำตอบ:


3

รหัสนี้อนุมานว่าทั้ง x0 และ x1 เป็นจุดปลายรวมและคำนั้นเป็น endian น้อย (เช่นพิกเซล (0,0) สามารถตั้งค่าได้ด้วยarray[0][0]|=1)

int line(word *array, int x0, int x1, int y) {
  word *line = array + (y << 4);
  word *start = line + (x0 >> 4);
  word *end = line + (x1 >> 4);
  word start_mask = (word)-1 << (x0 & 15);
  word end_mask = (unsigned word)-1 >> (15 - (x1 & 15));
  if (start == end) {
    *start |= start_mask & end_mask;
  } else {
    *start |= start_mask;
    *end |= end_mask;
    for (word *p = start + 1; p < end; p++) *p = (word)-1;
  }
}

1
เร็วแค่ไหน?
ผู้ใช้ที่ไม่รู้จัก

1

หลาม

เคล็ดลับหลักที่นี่คือการใช้ตารางการค้นหาเพื่อเก็บ bitmasks ของพิกเซล นี่เป็นการบันทึกการดำเนินการบางอย่าง ตาราง 1kB นั้นไม่ใหญ่มากสำหรับแพลตฟอร์มแบบฝังในปัจจุบัน

หากพื้นที่ว่างจริงๆสำหรับราคาสองถึงสามเท่าตารางการค้นหาสามารถลดลงเหลือเพียง 64B

รหัสนี้เป็น Python แต่จะง่ายต่อการย้ายไปยังภาษาใด ๆ ที่รองรับการทำงานของบิต

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

T=[65535, 32767, 16383, 8191, 4095, 2047, 1023, 511,
   255, 127, 63, 31, 15, 7, 3, 1]*16
U=[32768, 49152, 57344, 61440, 63488, 64512, 65024, 65280,
   65408, 65472, 65504, 65520, 65528, 65532, 65534, 65535]*16

def drawline(x1,x2,y):
    y_=y<<4
    x1_=y_+(x1>>4)
    x2_=y_+(x2>>4)
    if x1_==x2_:
        buf[x1_]|=T[x1]&U[x2]
        return    
    buf[x1_]|=T[x1]
    buf[x2_]|=U[x2]        
    x1_+=+1
    while x1_<x2_:
        buf[x1_] = 0xffff
        x1_+=1


#### testing code ####

def clear():
    global buf
    buf=[0]*192*16

def render():
    for y in range(192):
        print "".join(bin(buf[(y<<4)+x])[2:].zfill(16) for x in range(16))


clear()
for y in range(0,192):
    drawline(y/2,y,y)
for x in range(10,200,6):
    drawline(x,x+2,0)
    drawline(x+3,x+5,1)
for y in range(-49,50):
    drawline(200-int((2500-y*y)**.5), 200+int((2500-y*y)**.5), y+60)
render()

1

นี่คือ C version ของคำตอบ Python ของฉันโดยใช้คำสั่ง switch แทน while loop และการทำดัชนีที่ลดลงโดยการเพิ่มตัวชี้แทนที่จะเป็น index array

ขนาดของตารางการค้นหาสามารถลดลงอย่างมากโดยใช้ T [x1 & 0xf] และ U [x2 & 0xf] สำหรับคำแนะนำเพิ่มเติมสองสามข้อ

#include <stdio.h>
#include <math.h>

unsigned short T[] = {0xffff, 0x7fff, 0x3fff, 0x1fff, 0x0fff, 0x07ff, 0x03ff, 0x01ff,
                      0x00ff, 0x007f, 0x003f, 0x001f, 0x000f, 0x0007, 0x0003, 0x0001,
                      0xffff, 0x7fff, 0x3fff, 0x1fff, 0x0fff, 0x07ff, 0x03ff, 0x01ff,
                      0x00ff, 0x007f, 0x003f, 0x001f, 0x000f, 0x0007, 0x0003, 0x0001,
                      0xffff, 0x7fff, 0x3fff, 0x1fff, 0x0fff, 0x07ff, 0x03ff, 0x01ff,
                      0x00ff, 0x007f, 0x003f, 0x001f, 0x000f, 0x0007, 0x0003, 0x0001,
                      0xffff, 0x7fff, 0x3fff, 0x1fff, 0x0fff, 0x07ff, 0x03ff, 0x01ff,
                      0x00ff, 0x007f, 0x003f, 0x001f, 0x000f, 0x0007, 0x0003, 0x0001,
                      0xffff, 0x7fff, 0x3fff, 0x1fff, 0x0fff, 0x07ff, 0x03ff, 0x01ff,
                      0x00ff, 0x007f, 0x003f, 0x001f, 0x000f, 0x0007, 0x0003, 0x0001,
                      0xffff, 0x7fff, 0x3fff, 0x1fff, 0x0fff, 0x07ff, 0x03ff, 0x01ff,
                      0x00ff, 0x007f, 0x003f, 0x001f, 0x000f, 0x0007, 0x0003, 0x0001,
                      0xffff, 0x7fff, 0x3fff, 0x1fff, 0x0fff, 0x07ff, 0x03ff, 0x01ff,
                      0x00ff, 0x007f, 0x003f, 0x001f, 0x000f, 0x0007, 0x0003, 0x0001,
                      0xffff, 0x7fff, 0x3fff, 0x1fff, 0x0fff, 0x07ff, 0x03ff, 0x01ff,
                      0x00ff, 0x007f, 0x003f, 0x001f, 0x000f, 0x0007, 0x0003, 0x0001,
                      0xffff, 0x7fff, 0x3fff, 0x1fff, 0x0fff, 0x07ff, 0x03ff, 0x01ff,
                      0x00ff, 0x007f, 0x003f, 0x001f, 0x000f, 0x0007, 0x0003, 0x0001,
                      0xffff, 0x7fff, 0x3fff, 0x1fff, 0x0fff, 0x07ff, 0x03ff, 0x01ff,
                      0x00ff, 0x007f, 0x003f, 0x001f, 0x000f, 0x0007, 0x0003, 0x0001,
                      0xffff, 0x7fff, 0x3fff, 0x1fff, 0x0fff, 0x07ff, 0x03ff, 0x01ff,
                      0x00ff, 0x007f, 0x003f, 0x001f, 0x000f, 0x0007, 0x0003, 0x0001,
                      0xffff, 0x7fff, 0x3fff, 0x1fff, 0x0fff, 0x07ff, 0x03ff, 0x01ff,
                      0x00ff, 0x007f, 0x003f, 0x001f, 0x000f, 0x0007, 0x0003, 0x0001,
                      0xffff, 0x7fff, 0x3fff, 0x1fff, 0x0fff, 0x07ff, 0x03ff, 0x01ff,
                      0x00ff, 0x007f, 0x003f, 0x001f, 0x000f, 0x0007, 0x0003, 0x0001,
                      0xffff, 0x7fff, 0x3fff, 0x1fff, 0x0fff, 0x07ff, 0x03ff, 0x01ff,
                      0x00ff, 0x007f, 0x003f, 0x001f, 0x000f, 0x0007, 0x0003, 0x0001,
                      0xffff, 0x7fff, 0x3fff, 0x1fff, 0x0fff, 0x07ff, 0x03ff, 0x01ff,
                      0x00ff, 0x007f, 0x003f, 0x001f, 0x000f, 0x0007, 0x0003, 0x0001,
                      0xffff, 0x7fff, 0x3fff, 0x1fff, 0x0fff, 0x07ff, 0x03ff, 0x01ff,
                      0x00ff, 0x007f, 0x003f, 0x001f, 0x000f, 0x0007, 0x0003, 0x0001};

unsigned short U[] = {0x8000, 0xc000, 0xe000, 0xf000, 0xf800, 0xfc00, 0xfe00, 0xff00,
                      0xff80, 0xffc0, 0xffe0, 0xfff0, 0xfff8, 0xfffc, 0xfffe, 0xffff,
                      0x8000, 0xc000, 0xe000, 0xf000, 0xf800, 0xfc00, 0xfe00, 0xff00,
                      0xff80, 0xffc0, 0xffe0, 0xfff0, 0xfff8, 0xfffc, 0xfffe, 0xffff,
                      0x8000, 0xc000, 0xe000, 0xf000, 0xf800, 0xfc00, 0xfe00, 0xff00,
                      0xff80, 0xffc0, 0xffe0, 0xfff0, 0xfff8, 0xfffc, 0xfffe, 0xffff,
                      0x8000, 0xc000, 0xe000, 0xf000, 0xf800, 0xfc00, 0xfe00, 0xff00,
                      0xff80, 0xffc0, 0xffe0, 0xfff0, 0xfff8, 0xfffc, 0xfffe, 0xffff,
                      0x8000, 0xc000, 0xe000, 0xf000, 0xf800, 0xfc00, 0xfe00, 0xff00,
                      0xff80, 0xffc0, 0xffe0, 0xfff0, 0xfff8, 0xfffc, 0xfffe, 0xffff,
                      0x8000, 0xc000, 0xe000, 0xf000, 0xf800, 0xfc00, 0xfe00, 0xff00,
                      0xff80, 0xffc0, 0xffe0, 0xfff0, 0xfff8, 0xfffc, 0xfffe, 0xffff,
                      0x8000, 0xc000, 0xe000, 0xf000, 0xf800, 0xfc00, 0xfe00, 0xff00,
                      0xff80, 0xffc0, 0xffe0, 0xfff0, 0xfff8, 0xfffc, 0xfffe, 0xffff,
                      0x8000, 0xc000, 0xe000, 0xf000, 0xf800, 0xfc00, 0xfe00, 0xff00,
                      0xff80, 0xffc0, 0xffe0, 0xfff0, 0xfff8, 0xfffc, 0xfffe, 0xffff,
                      0x8000, 0xc000, 0xe000, 0xf000, 0xf800, 0xfc00, 0xfe00, 0xff00,
                      0xff80, 0xffc0, 0xffe0, 0xfff0, 0xfff8, 0xfffc, 0xfffe, 0xffff,
                      0x8000, 0xc000, 0xe000, 0xf000, 0xf800, 0xfc00, 0xfe00, 0xff00,
                      0xff80, 0xffc0, 0xffe0, 0xfff0, 0xfff8, 0xfffc, 0xfffe, 0xffff,
                      0x8000, 0xc000, 0xe000, 0xf000, 0xf800, 0xfc00, 0xfe00, 0xff00,
                      0xff80, 0xffc0, 0xffe0, 0xfff0, 0xfff8, 0xfffc, 0xfffe, 0xffff,
                      0x8000, 0xc000, 0xe000, 0xf000, 0xf800, 0xfc00, 0xfe00, 0xff00,
                      0xff80, 0xffc0, 0xffe0, 0xfff0, 0xfff8, 0xfffc, 0xfffe, 0xffff,
                      0x8000, 0xc000, 0xe000, 0xf000, 0xf800, 0xfc00, 0xfe00, 0xff00,
                      0xff80, 0xffc0, 0xffe0, 0xfff0, 0xfff8, 0xfffc, 0xfffe, 0xffff,
                      0x8000, 0xc000, 0xe000, 0xf000, 0xf800, 0xfc00, 0xfe00, 0xff00,
                      0xff80, 0xffc0, 0xffe0, 0xfff0, 0xfff8, 0xfffc, 0xfffe, 0xffff,
                      0x8000, 0xc000, 0xe000, 0xf000, 0xf800, 0xfc00, 0xfe00, 0xff00,
                      0xff80, 0xffc0, 0xffe0, 0xfff0, 0xfff8, 0xfffc, 0xfffe, 0xffff,
                      0x8000, 0xc000, 0xe000, 0xf000, 0xf800, 0xfc00, 0xfe00, 0xff00,
                      0xff80, 0xffc0, 0xffe0, 0xfff0, 0xfff8, 0xfffc, 0xfffe, 0xffff};

unsigned short buf[192*16];

void clear(){
    int i;
    for (i=0; i<192*16; i++) buf[i]==0;
}

void render(){
    int x,y;
    for (y=0; y<192; y++){
        for (x=0; x<256; x++) printf("%d", (buf[(y<<4)+(x>>4)]>>(15-(x&15)))&1);
        printf("\n");
    }
}

void drawline(int x1, int x2, int y){
    int y_ = y<<4;
    int x1_ = y_+(x1>>4);
    int x2_ = y_+(x2>>4);
    unsigned short *p = buf+x1_;

    if (x1_==x2_){
        *p|=T[x1]&U[x2];
        return;
        }

    *p++|=T[x1];
    switch (x2_-x1_){
    case 14: *p++ = 0xffff;
    case 13: *p++ = 0xffff;
    case 12: *p++ = 0xffff;
    case 11: *p++ = 0xffff;
    case 10: *p++ = 0xffff;
    case 9: *p++ = 0xffff;
    case 8: *p++ = 0xffff;
    case 7: *p++ = 0xffff;
    case 6: *p++ = 0xffff;
    case 5: *p++ = 0xffff;
    case 4: *p++ = 0xffff;
    case 3: *p++ = 0xffff;
    case 2: *p++ = 0xffff;
    case 1: *p++ = U[x2];
    }     
}


int main(){
    int x,y;
    clear();

    for (y=0; y<192; y++){
        drawline(y/2,y,y); 
    }

    for (x=10; x<200; x+=6){
        drawline(x,x+2,0);
        drawline(x+3,x+5,1);
    }

    for (y=-49; y<50; y++){
        x = sqrt(2500-y*y);
        drawline(200-x, 200+x, y+60);
    }
    render();
    return 0;
    }

เร็วแค่ไหน?
ผู้ใช้ที่ไม่รู้จัก

@ ผู้ใช้ที่ไม่รู้จัก, สตริงยาวเท่าไหร่? ฉันคิดว่ามันควรจะเร็วกว่าคำตอบที่ยอมรับเพราะใช้ตารางการค้นหาเพื่อลดปริมาณงานเล็กน้อย ทำไมคุณไม่ลองพวกเขาและแจ้งให้เราทราบสิ่งที่คุณพบ?
gnibbler

1

Scala, 7s / 1M lines 4.1s / 1M lines

// declaration and initialisation of an empty field: 
val field = Array.ofDim[Short] (192, 16) 

การใช้งานครั้งแรก:

// util-method: set a single Bit:
def setBit (x: Int, y: Int) = 
  field (y)(x/16) = (field (y)(x/16) | (1 << (15 - (x % 16)))).toShort 
def line (x0: Int, x1: Int, y: Int) = 
  (x0 to x1) foreach (setBit (_ , y))

หลังจากกำจัดวิธีการเรียกภายในและแทนที่ for- ด้วย while-loop บน 2Ghz Single Core ของฉันกับ Scala 2.8 มันช่วยให้ 1 Mio เส้นใน 4.1 วินาทีวินาที แทน 7s เริ่มต้น

  def line (x0: Int, x1: Int, y: Int) = {
    var x = x0
    while (x < x1) {  
      field (y)(x/16) = (field (y)(x/16) | (1 << (15 - (x % 16)))).toShort
      x += 1
    }
  }

รหัสทดสอบและการร้องขอ:

// sample invocation:
line (12, 39, 3) 
// verification 
def shortprint (s: Short) = s.toBinaryString.length match {          
  case 16 => s.toBinaryString                                          
  case 32 => s.toBinaryString.substring (16)                           
  case x  => ("0000000000000000".substring (x) + s.toBinaryString)}

field (3).take (5).foreach (s=> println (shortprint (s)))            
// result:
0000000000001111
1111111111111111
1111111100000000
0000000000000000
0000000000000000

การทดสอบประสิทธิภาพ:

  val r = util.Random 

  def testrow () {
    val a = r.nextInt (256)
    val b = r.nextInt (256)
    if (a < b)
      line (a, b, r.nextInt (192)) else
        line (b, a, r.nextInt (192)) 
  }

  def test (count: Int): Unit = {
    for (n <- (0 to count))
      testrow ()
  }

  // 1 mio tests
  test (1000*1000) 

ทดสอบด้วยเวลาเครื่องมือ unix เปรียบเทียบเวลาผู้ใช้รวมถึงเวลาเริ่มต้นโค้ดที่คอมไพล์ไม่มีเฟสเริ่มต้น JVM

การเพิ่มจำนวนบรรทัดแสดงว่าสำหรับทุกๆล้านใหม่จะต้องมี 3.3s พิเศษ

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