# a general structure for a seach |
|
SEARCH: while ( 1 ) { |
|
# keep searching until we find what we want or discover it |
# isn't there |
|
# stop if the thing we are looking cannot be found |
if ( search space is empty ) { |
# thing we are looking for is not present |
last SEARCH; |
} |
|
# probe the search space, that is, pick a spot in the search |
# space and take a look |
|
# stop if we find it |
if ( thing found ) { |
# thing we are looking for is present |
last SEARCH; |
} |
|
# narrow down the search space and keep looking |
|
} |
[0,100]
[51, 100]
[76, 100]
[76, 87]
[82, 87]
[82, 85]
/* |
|
Binary search |
|
Given a list of integers of length len, and a key, determine if key is |
present in list and return the index of key. If not present, return -1 |
|
Pre Conditions: |
list is sorted in ascending order |
|
*/ |
|
int bsearch_int(int list[], int len, int key) { |
int low = 0; |
int high = len; |
|
int probe; |
|
/* invariant: |
If present, key is in the index range [low, high), |
i.e. key is one of list[low], ..., list[high-1] |
|
*/ |
|
/* keep searching until the search interval is empty */ |
while ( low < high ) { |
/* pick a probe point in the middle of the interval |
we assume we do not get integer overflow here, which is safe |
to assume on the Arduino |
|
Invariant: low <= probe < high |
*/ |
probe = (high + low) / 2; |
|
if ( key == list[probe] ) { |
/* found it */ |
return probe; |
} |
|
/* determine which side of probe the key might lie */ |
if ( key < list[probe] ) { |
high = probe; |
} |
else { |
low = probe + 1; |
} |
|
} // keep looking |
|
/* if the search interval is empty, the key is not present */ |
return -1; |
} |
x | number of divisions by 2 before <=2 |
lg(x) |
1 | 0 | 0 |
2 | 1 | 1 |
3 | 2 | 1.584 |
4 | 2 | 2 |
1024 | 10 | 10 |
1025 | 10 | 10.00141 |
2047 | 10 | 10.99930 |
2048 | 11 | 11 |
1048576 | 20 | 20 |
bsearch
. It will find a single matching object in an ordered list, or report that there is no such object. If there are multiple objects with the same key, it will find one of them. Finding all of them requires multiple calls, and some clever manipulation of search intervals. void * bsearch(const void *key,
const void *base, size_t nel, size_t width,
int (*compar) (const void *, const void *));
The bsearch()
function searches an array of nel objects, the initial member of which is pointed to by base
, for a member that matches the object pointed to by key. The size (in bytes) of each member of the array is specified by width
. compar
. The compar
routine is expected to have two arguments which point to the key object and to an array member, in that order. It should return an integer which is less than, equal to, or greater than zero if the key object is found, respec- tively, to be less than, to match, or be greater than the array member. bsearch()
function returns a pointer to a matching member of the array, or a null pointer if no match is found. If two members compare as equal, which member is matched is unspecified. #include <Arduino.h> |
#include <stdlib.h> |
|
// type of things to be sorted and searched |
typedef int item_t; |
|
void print_item(item_t a) { |
Serial.print(a); |
} |
|
void print_list(item_t *list, size_t len) { |
for (size_t i=0; i < len; i++) { |
print_item(list[i]); |
Serial.print(" "); |
} |
Serial.println(); |
} |
|
// the comparison function for things of type item_t |
int item_compare (const item_t *a, const item_t *b) { |
// Question: what happens if we return a-b instead? |
return *a - *b; |
} |
|
// the test to see if a list of things of type item_t is in ascending order |
uint8_t is_in_order(item_t *list, size_t len) { |
for (size_t i=0; i < len-1; i++) { |
if ( item_compare(&list[i], &list[i+1]) > 0 ) { |
// out of order |
return 0; |
} |
} |
// ok, it's in order |
return 1; |
} |
|
// Question: how would you write a generic is_in_order function similar to |
// qsort and bsearch? |
|
void setup() { |
size_t n_items = 32; |
item_t list[n_items]; |
|
item_t e_first; |
item_t e_mid; |
item_t e_last; |
|
Serial.begin(9600); |
|
// generate a random list of item_t things |
for (size_t i=0; i < n_items; i++) { |
list[i] = random(0, 1000); |
} |
|
print_list(list, n_items); |
|
// pick the original first, mid, and last elements |
e_first = list[0]; |
e_mid = list[n_items/2]; |
e_last = list[n_items-1]; |
|
Serial.print("first "); print_item(e_first); |
Serial.print(" mid "); print_item(e_mid); |
Serial.print(" last "); print_item(e_last); |
Serial.println(); |
|
// now sort them |
qsort(list, n_items, sizeof(item_t), (__compar_fn_t) item_compare); |
|
print_list(list, n_items); |
|
// are they sorted? |
if ( is_in_order(list, n_items) ) { |
Serial.println("OK, list is in order"); |
} |
else { |
Serial.println("ERROR, list is out of order"); |
} |
|
// find the sorted positions of the first, mid, and last |
|
// note that bsearch returns a pointe to the item, but you can |
// get it's index by simply subtracting the base address of the list |
|
item_t *i_first = |
(item_t *) bsearch(&e_first, list, n_items, sizeof(item_t), |
(__compar_fn_t) item_compare); |
|
item_t *i_mid = |
(item_t *) bsearch(&e_mid, list, n_items, sizeof(item_t), |
(__compar_fn_t) item_compare); |
|
item_t *i_last = |
(item_t *) bsearch(&e_last, list, n_items, sizeof(item_t), |
(__compar_fn_t) item_compare); |
|
// note the subtraction to get the index |
|
Serial.print("i_first "); Serial.print(i_first - list); |
Serial.print(" i_mid "); Serial.print(i_mid - list); |
Serial.print(" i_last "); Serial.print(i_last - list); |
Serial.println(); |
|
// now let's look for something that is not there |
item_t missing = -1; |
|
item_t *i_missing = |
(item_t *) bsearch(&missing, list, n_items, sizeof(item_t), |
(__compar_fn_t) item_compare); |
Serial.print("Result of search for "); print_item(missing); |
Serial.print(" is " ); Serial.print((int) i_missing); |
Serial.println(); |
|
|
} |
|
void loop() { |
} |
// type of things to be sorted and searched
typedef struct {
int16_t x;
int16_t y;
} item_t;
Now suppose that the ordering rule is as follows: that you order by element x
first, and if the x
values match, then you refine the order by comparing the y
value. So for example (1,2) < (2,3)
and (1,2) < (1,3)
. x
values, chose them randomly from a small range, say 0 to 4.
13. Searching Tangible Computing / Version 3.20 2013-03-25 |