Competitive/Collaborative Programming Class

ICPC Computer Programming Contest Prep

Problem Solving in Computer Science

Spring 2024 -- CSC 2700 Section 01 (1218 Patrick Taylor, 6:30 PM - 8:20 PM)



Continuum of a Program

If you look a program, you will see data and code -- logical. Have you ever thought about their interrelationships?

The choice of data structure predestines some of the code. The choice of some code predestines some data structures. In fact, the two could be perceived as opposite ends of a continuum. When you design/implement a program, you choose what to do. Consider that choice a slider on a scale. It can be heavy data structure and lighter code or the other way around.

Programs can be optimized for speed or for data usage (usually these goals are diametrically opposed).

Here are 3 different implementations that produce the same result:

Nested If Statements

if (1 == i)
 { total = total + 17; }
else if (2 == i)
 { total = total + 29; }
else if (3 == i)
 { total = total + 99; }

Case Statement

switch (i)
 { // switch
 case 1: total = total + 17; break;
 case 2: total = total + 29; break;
 case 3: total = total + 99; break;
 } // switch
 

Array Implementation

 int ary[4] {0, 17, 29, 99};


 total = total + ary[i];
 
Assembly code for If:
        movq    %rsp, %rbp      #,
        .cfi_def_cfa_register 6
        cmpl    $1, -8(%rbp)    #, i
        jne     .L2     #,
        addl    $17, -4(%rbp)   #, total
        jmp     .L3     #
.L2:
        cmpl    $2, -8(%rbp)    #, i
        jne     .L4     #,
        addl    $29, -4(%rbp)   #, total
        jmp     .L3     #
.L4:
        cmpl    $3, -8(%rbp)    #, i
        jne     .L3     #,
        addl    $99, -4(%rbp)   #, total
.L3:
        movl    $0, %eax        #, D.2060
        leave
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc
.LFE0:
Assembly code for Case:
        movq    %rsp, %rbp      #,
        .cfi_def_cfa_register 6
        movl    -8(%rbp), %eax  # i, tmp60
        cmpl    $2, %eax        #, tmp60
        je      .L4     #,
        cmpl    $3, %eax        #, tmp60
        je      .L5     #,
        cmpl    $1, %eax        #, tmp60
        jne     .L2     #,
.L3:
        addl    $17, -4(%rbp)   #, total
        jmp     .L2     #
.L4:
        addl    $29, -4(%rbp)   #, total
        jmp     .L2     #
.L5:
        addl    $99, -4(%rbp)   #, total
.L2:
        movl    $0, %eax        #, D.2057
        leave
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc
.LFE0:  
Assembly for array example:
        movq    %rsp, %rbp      #,
        .cfi_def_cfa_register 6
        movl    $0, -32(%rbp)   #, ary
        movl    $17, -28(%rbp)  #, ary
        movl    $29, -24(%rbp)  #, ary
        movl    $99, -20(%rbp)  #, ary
        movl    -8(%rbp), %eax  # i, i.0
        cltq
        movl    -32(%rbp,%rax,4), %eax  # ary, D.2054
        addl    %eax, -4(%rbp)  # D.2054, total
        movl    $0, %eax        #, D.2055
        leave
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc
.LFE0:

Here are some code segments to perform the same task. Look at them.

Nested ifs Using case:
#define LEFT 0
#define RIGHT 1

#define NORTH 0
#define EAST  1
#define SOUTH 2
#define WEST  3

/* direction is left or right */
.
.
.
if (NORTH == curMove)
   if (LEFT == direction)
      nextMove = WEST;
   else
      nextMove = EAST;
else
if (EAST == curMove)
   if (LEFT == direction)
      nextMove = NORTH;
   else
      nextMove = SOUTH;
else
if (SOUTH == curMove)
   if (LEFT == direction)
      nextMove = EAST;
   else
      nextMove = WEST;
else
if (WEST == curMove)
   if (LEFT == direction)
      nextMove = SOUTH;
   else
      nextMove = NORTH;
#define LEFT 0
#define RIGHT 1

#define NORTH 0
#define EAST  1
#define SOUTH 2
#define WEST  3

/* direction is left or right */
.
.
.
switch (curMove)
 { // switch
   case NORTH: 
        if (LEFT == direction)
           nextMove = WEST;
        else
           nextMove = EAST;
        break;
   case EAST: 
        if (LEFT == direction)
           nextMove = NORTH;
        else
           nextMove = SOUTH;
        break;
   case SOUTH: 
        if (LEFT == direction)
           nextMove = EAST;
        else
           nextMove = WEST;
        break;
   case WEST: 
        if (LEFT == direction)
           nextMove = SOUTH;
        else
           nextMove = NORTH;
        break;
 } // switch
Using 2D array Using 1D array
#define LEFT 0
#define RIGHT 1

#define NORTH 0
#define EAST  1
#define SOUTH 2
#define WEST  3

int next[4][2];

next[NORTH][LEFT]  = WEST;
next[NORTH][RIGHT] = EAST;
next[EAST][LEFT]   = NORTH;
next[EAST][RIGHT]  = SOUTH;
next[SOUTH][LEFT]  = EAST;
next[SOUTH][RIGHT] = WEST;
next[WEST][LEFT]   = SOUTH;
next[WEST][RIGHT]  = NORTH;

/* direction is left or right */
.
.
.
nextMove = next[curMove][direction];
#define LEFT 0
#define RIGHT 1

#define NORTH 0
#define EAST  2
#define SOUTH 4
#define WEST  6

int next[8];

next[NORTH + LEFT]  = WEST;
next[NORTH + RIGHT] = EAST;
next[EAST  + LEFT]  = NORTH;
next[EAST  + RIGHT] = SOUTH;
next[SOUTH + LEFT]  = EAST;
next[SOUTH + RIGHT] = WEST;
next[WEST  + LEFT]  = SOUTH;
next[WEST  + RIGHT] = NORTH;

/* direction is left or right */
.
.
.
nextMove = next[curMove + direction];
Using clock arithmetic  
#define LEFT  3
#define RIGHT 1

#define NORTH 0
#define EAST  1
#define SOUTH 2
#define WEST  3

/* direction is left or right */
.
.
.
nextMove = (curMove + direction) MOD 4;
 

Above are five different ways of doing the same thing. Consider the code.

Realize that computers (cpus) tend to have many common features even though they do differ in implementation. Here are some of the features that are common but differ in size:

Reducing size of code and data after compilation increases the probability that the code/data will be in cache (dramatically improving runtime. Branching can destroy performance by not using prefetch or clearing a pipeline.