Example sets: | |
a SET same: [none] different: number, color, shape, fill
| |
a SET same: number, color, shape different: fill
| |
a SET same: number, shape different: color, fill
| |
NOT a set same: number, shape different: color wrong: fill | |
To make it a set, one of the open fills needs to
be solid, or the striped fill needs to be open.
| |
a SET same: shape different: color, number, fill
| |
NOT a set same: shape, number different: fill wrong: color | |
To make it a set, one of the red colors needs to
be blue, or the green color needs to be red.
|
public static void compute_components( int card, int[] components ) { components[0] = card % 3; components[1] = card % 9 / 3; components[2] = card % 27 / 9; components[3] = card / 27; }
card1 0 0 1 1 2 2 0 0 1 2 card2 0 1 1 2 2 0 1 0 1 2 card3 1 1 2 2 0 0 2 0 1 2 set? no no no no no no yes yes yes yesAbove are all combinations of three 3-valued variables. Is a pattern or source of leverage evident?
Here is an implementation ...
boolean is_a_set( int[] selected_cards ) { int[][] cards_and_components = new int[3][4]; for (int i=0; i < 3; i++) compute_components( selected_cards[i], cards_and_components[i] ); for (int sum, i=0; i < 4; i++) { sum = 0; for (int j=0; j < 3; j++) sum += cards_and_components[j][i]; // If any of the 4 sums is not evenly divisible by 3, then the 3 cards do not form a set if (sum % 3 != 0) return false; } return true; }
Here is an implementation ...
int number_of_sets = 0; for (int i=0; i < cards_showing.length - 2; i++) for (int j=i+1; j < cards_showing.length - 1; j++) for (int k=j+1; k < cards_showing.length; k++) { test_set[0] = cards_showing[i]; test_set[1] = cards_showing[j]; test_set[2] = cards_showing[k]; if (is_a_set( test_set )) { number_of_sets++; answer.append(test_set[0]+1).append(' '); answer.append(test_set[1]+1).append(' '); answer.append(test_set[2]+1).append(" "); } } return number_of_sets;
// incremental examples of loops, and growing to an algorithm for // "all combinations of 12 things taken 3 at a time" #include <iostream> #include <string> #include <sstream> using std::cout; using std::string; using std::stringstream; #if 0 // "for" loop, "+=" operator, one-line block of code doesn't need "{ }" int main( void ) { for (int number = 1; number < 11; number += 1) cout << number << ' '; cout << '\n'; } // 1 2 3 4 5 6 7 8 9 10 // one loop inside another loop, accessing arrays int main( void ) { int nums[] = { 1,2,3,4,5 }; char chars[] = { 'a','b','c','d','e' }; for (int i=0; i < 5; ++i) { for (int j=0; j < 5; ++j) cout << nums[i] << chars[j] << ' '; cout << '\n'; } } // 1a 1b 1c 1d 1e // 2a 2b 2c 2d 2e // 3a 3b 3c 3d 3e // 4a 4b 4c 4d 4e // 5a 5b 5c 5d 5e // defining and loading a 2-D array, using "integer division" that produces 0 int main( void ) { int matrix[4][4]; for (int i=1; i <= 4; ++i) for (int j=1; j <= 4; ++j) // if i < j then 0, if j < i then 0, if i == j then 1 matrix[i-1][j-1] = (i/j) * (j/i); for (int i=0; i < 4; ++i) { for (int j=0; j < 4; ++j) cout << matrix[i][j] << ' '; cout << '\n'; } } // 1 0 0 0 // 0 1 0 0 // 0 0 1 0 // 0 0 0 1 // each inner loop is "sized" by its outer loop int main() { cout << "all combinations of 5 things taken 3 at a time\n"; for (int i=0; i < 5 - 2; i++) for (int j=i+1; j < 5 - 1; j++) for (int k=j+1; k < 5; k++) cout << i << j << k << " "; cout << '\n'; } // all combinations of 5 things taken 3 at a time ... 5 * 4 * 3 / 3 * 2 * 1 // 012 013 014 023 024 034 123 124 134 234 // all combinations of 12 things taken 3 at a time ... 12 * 11 * 10 / 3 * 2 * 1 int main( void ) { // equals 220 int count = 0, lines = 0; char cards[] = { '1','2','3','4','5','6','7','8','9','a','b','c' }; for (int i=0; i < 12 - 2; ++i) for (int j=i+1; j < 12 - 1; ++j) { for (int k=j+1; k < 12; ++k) { cout << cards[i] << cards[j] << cards[k] << ' '; count += 1; } cout << '\n'; lines += 1; } cout << "count is " << count << ", lines is " << lines << '\n'; } // 123 124 125 126 127 128 129 12a 12b 12c // 134 135 136 137 138 139 13a 13b 13c // 145 146 147 148 149 14a 14b 14c // 156 157 158 159 15a 15b 15c // 167 168 169 16a 16b 16c // 178 179 17a 17b 17c // 189 18a 18b 18c // 19a 19b 19c // 1ab 1ac // 1bc // 234 235 236 237 238 239 23a 23b 23c // 245 246 247 248 249 24a 24b 24c // ... // 89a 89b 89c // 8ab 8ac // 8bc // 9ab 9ac // 9bc // abc // count is 220, lines is 55 #endif // all permutations of the combinations of 5 things taken 3 at a time void permute(string& str, int l, int r); void swap( char& one, char& two ); int count = 0; int main( void ) { cout << "all permutations of 5 things taken 3 at a time\n"; cout << " (a permutation is an ordered combination)\n"; for (int i=0; i < 5 - 2; i++) for (int j=i+1; j < 5 - 1; j++) for (int k=j+1; k < 5; k++) { stringstream ss; ss << i << j << k; string str = ss.str(); permute( str, 0, str.size() - 1 ); cout << '\n'; } cout << "count is " << count << '\n'; } // all permutations of 5 things taken 3 at a time ... 5 * 4 * 3 // (a permutation is an ordered combination) // 012 021 102 120 210 201 // 013 031 103 130 310 301 // 014 041 104 140 410 401 // 023 032 203 230 320 302 // 024 042 204 240 420 402 // 034 043 304 340 430 403 // 123 132 213 231 321 312 // 124 142 214 241 421 412 // 134 143 314 341 431 413 // 234 243 324 342 432 423 // count is 60 // https://www.geeksforgeeks.org/write-a-c-program-to-print-all-permutations-of-a-given-string/ void permute(string& str, int l, int r) { if (l == r) { cout << str << ' '; count += 1; } else for (int i = l; i <= r; i++) { swap( str[l], str[i] ); //cout << 'p' << l << i << ' '; permute( str, l + 1, r ); // backtrack //cout << "b "; swap( str[l], str[i] ); } } void swap( char& one, char& two ) { char temp; temp = one; one = two; two = temp; } // A loop can often be used in place of many "if/else" statements. // Many "if" statements in the 1960s language PL/I DATES: PROC OPTIONS (MAIN); READ: GET DATA (IYEAR, IDATE); IF IDATE < 1 | IDATE > 366 | IYEAR < 0 THEN RETURN; IF IDATE <= 31 THEN GO TO JAN; L = 1; I = IYEAR/400; IF I = IYEAR/400 THEN GO TO LEAP; I = IYEAR/100; IF I = IYEAR/100 THEN GO TO NOLEAP; I = IYEAR/4; IF I = IYEAR/4 THEN GO TO LEAP; NOLEAP: L=0; IF IDATE > 365 THEN RETURN; LEAP: IF IDATE > 181 + L THEN GO TO G181; IF IDATE > 90 + L THEN GO TO G90; IF IDATE > 59 + L THEN GO TO G59; MONTH = 2; IDAY = IDATE - 31; GO TO OUT; G59: MONTH = 3; IDAY = IDATE - (59 + L); GO TO OUT; G90: IF IDATE > 120 + L THEN GO TO G120; MONTH = 4; IDAY = IDATE - (90 + L); GO TO OUT; G120: IF IDATE > 151 + L THEN GO TO G151; MONTH = 5; IDAY = IDATE - (120 + L); GO TO OUT; G151: MONTH = 6; IDAY = IDATE - (151 + L); GO TO OUT; G181: IF IDATE > 273 + L THEN GO TO G273; IF IDATE > 243 + L THEN GO TO G243; IF IDATE > 212 + L THEN GO TO G212; MONTH = 7; IDAY = IDATE - (181 + L); GO TO OUT; G212: MONTH = 8; IDAY = IDATE - (212 + L); GO TO OUT; G243: MONTH = 9; IDAY = IDATE - (243 + L); GO TO OUT; G273: IF IDATE > 334 + L THEN GO TO G334; IF IDATE > 304 + L THEN GO TO G304; MONTH = 10; IDAY = IDATE - (273 + L); GO TO OUT; G304: MONTH = 11; IDAY = IDATE - (304 + L); GO TO OUT; G334: MONTH = 12; IDAY = IDATE - (334 + L); GO TO OUT; OUT: PUT DATA (MONTH,IDAY,IYEAR) SKIP; GO TO READ; JAN: MONTH=1; IDAY=IDATE; GOTO OUT; END DATES; // Many "if" stmts replaced with: days_per_month_table data structure, and a loop String convert_julian( int year, int julian ) { int[][] days_per_month_table = { {0,31,28,31,30,31,30,31,31,30,31,30,31}, {0,31,29,31,30,31,30,31,31,30,31,30,31} }; int leap = (year % 400 == 0 || year % 4 == 0 && year % 100 != 0) ? 1 : 0; int i; for (i=1; julian > days_per_month_table[leap][i]; i++) julian -= days_per_month_table[leap][i]; int yr = year - (year/100 * 100); return fill(i) + '/' + fill(julian) + '/' + fill(yr); } System.out.println( convert_julian( 1900, 60 ) ); // 03/01/00 System.out.println( convert_julian( 2000, 60 ) ); // 02/29/00 System.out.println( convert_julian( 2002, 60 ) ); // 03/01/02 System.out.println( convert_julian( 2004, 60 ) ); // 02/29/04 System.out.println( convert_julian( 1900, 365 ) ); // 12/31/00 System.out.println( convert_julian( 2000, 365 ) ); // 12/30/00 System.out.println( convert_julian( 2002, 365 ) ); // 12/31/02 System.out.println( convert_julian( 2004, 365 ) ); // 12/30/04 // the card game called Set: how to decide if 3 cards are a set // 2 implementations: 4 levels of "if" statements, or, a loop and ingenuity struct Card { int dimensions[4]; }; // 0-shape, 1-number, 2-color, 3-fill // An implementation using 4 levels of "if" statements #includeusing std::set; bool is_a_set( Card one, Card two, Card three ) { // the set data structure only stores unique values // (if values 1,1,2 are inserted, then values 1,2 are remembered) set accumulate[4]; accumulate[0].insert( one.dimensions[0] ); accumulate[0].insert( two.dimensions[0] ); accumulate[0].insert( three.dimensions[0] ); accumulate[1].insert( one.dimensions[1] ); accumulate[1].insert( two.dimensions[1] ); accumulate[1].insert( three.dimensions[1] ); accumulate[2].insert( one.dimensions[2] ); accumulate[2].insert( two.dimensions[2] ); accumulate[2].insert( three.dimensions[2] ); accumulate[3].insert( one.dimensions[3] ); accumulate[3].insert( two.dimensions[3] ); accumulate[3].insert( three.dimensions[3] ); // success is: each of the 4 dimensions across all 3 cards must be: // all the same value, or all different values if (accumulate[0].size() == 1 || accumulate[0].size() == 3) if (accumulate[1].size() == 1 || accumulate[1].size() == 3) if (accumulate[2].size() == 1 || accumulate[2].size() == 3) if (accumulate[3].size() == 1 || accumulate[3].size() == 3) return true; return false; } // An implementation using a loop and an ingenious algorithm // card1 0 0 1 1 2 2 0 0 1 2 // card2 0 1 1 2 2 0 1 0 1 2 // card3 1 1 2 2 0 0 2 0 1 2 // sum 1 2 4 5 4 2 3 0 3 6 // % 3 1 2 1 2 1 2 0 0 0 0 // set? no no no no no no yes yes yes yes // algorithm: use the modulus operator to "compute" the desired decision bool is_a_set( Card one, Card two, Card three ) { // This loop replaces the 4 "if" statements in the previous implementation for (int i=0; i < 4; ++i) // this "algorithm" demonstrates insight and leverage // if any dimension fails the set criteria, then return false immediately if ((one.dimensions[i] + two.dimensions[i] + three.dimensions[i]) % 3 != 0) return false; // only return true if all 4 dimensions satisfy the criteria to be a set return true; } // Generate and evaluate all combinations of 12 cards taken 3 at a time int compute_sets_present( Card cards_showing[], string& answer_str ) { int number_of_sets = 0; stringstream answer; for (int i=0; i < 12 - 2; i++) for (int j=i+1; j < 12 - 1; j++) for (int k=j+1; k < 12; k++) if (is_a_set( cards_showing[i],cards_showing[j],cards_showing[k] )) { number_of_sets++; answer << (i+1) << ' '; answer << (j+1) << ' '; answer << (k+1) << " "; } answer_str = answer.str(); return number_of_sets; } void initialize_card( int index, Card& card ) { // use modulus operator and integer division to compute the card's 4 dimensions card.dimensions[0] = index % 3; card.dimensions[1] = index % 9 / 3; card.dimensions[2] = index % 27 / 9; card.dimensions[3] = index / 27; } int main( void ) { Card cards[81]; for (int i=0; i < 81; ++i) initialize_card( i, cards[i] ); string answer_str; int num_sets = compute_sets_present( cards, answer_str ); cout << "number of sets is " << num_sets << '\n'; cout << answer_str << '\n'; } // number of sets is 13 // 1 2 3 1 4 7 1 5 9 1 6 8 2 4 9 2 5 8 2 6 7 // 3 4 8 3 5 7 3 6 9 4 5 6 7 8 9 10 11 12