How to calculate Big O Execution Growth Rate? - time-complexity

I just want to know the break down for the Big O execution growth rate for this code, I have try to calculate it but it I got the for loops wrong. so I am completely stuck on this now.
void doInter(int setA[], int setB[], int sizeA, int sizeB)
{
const int MAX = 10;
int sizeR;
int results [MAX];
// validate sizeA and sizeB
if ((sizeA == 0) || (sizeB == 0))
{
cout << "one of the sets is empty\n";
}
// save elements common to both sets
for (int i = sizeR = 0; i < sizeA; i++ )
{
if (member(setB, setA[i],sizeB))
{
results[sizeR++] = setA[i];
}
}
{
cout << results[i] << " ";
}
cout << "}" << endl;
}
bool member (int set[], int n, int size)
{
for (; size > 0; --size)
{
if (set[size-1] == n)
{
return true;
}
}
return false;
}

The complexity of this code is O(sizeA * sizeB). It is relatively easy to compute - first compute the complexity of the inner function member - this is a single cycle and in the worst case it will perform sizeB iterations. Now in the outer function you call this function in a cycle of size sizeA. Thus the overall complexity is the two complexities multiplied. The remaining operations are relatively simple with regards to this two cycles.
Also an example where this complexity is achieved is easy to see - use two arrays with no common elements.

Related

Comparing Execution time with Time Complexity in Merge & Quick Sort

I have implemented Merge & Quick Sort in the textbook what I've learned, and it says Time Complexities of each sorts are like this:
Merge Sort: O(n.log(n)) / Quick Sort: average O(n.log(n)) and O(n2) in the worst case (if key array is sorted).
So I executed the programs with Two types of Arrays: sorted and random, with different sizes.
Since I wanted to get the Average time, I have tried 10 times per each case.
Here is the code of Merge & Quick Sort:
#include <iostream>
#include <ctime>
#include <vector>
#include <algorithm>
using namespace std;
void Merge(vector<int>& s, int low, int mid, int high) {
int i = low;
int j = mid + 1;
int k = low;
vector<int> u(s);
while (i <= mid && j <= high) {
if (s.at(i) < s.at(j)) {
u.at(k) = s.at(i);
i++;
} else {
u.at(k) = s.at(j);
j++;
}
k++;
}
if (i > mid) {
for (int a = j; a < high + 1; a++) {
u.at(k) = s.at(a);
k++;
}
} else {
for (int a = i; a < mid + 1; a++) {
u.at(k) = s.at(a);
k++;
}
}
for (int a = low; a < high + 1; a++)
s.at(a) = u.at(a);
}
void MergeSort(vector<int>& s, int low, int high) {
int mid;
if (low < high) {
mid = (low + high) / 2;
MergeSort(s, low, mid);
MergeSort(s, mid + 1, high);
Merge(s, low, mid, high);
}
}
void swap(int& a, int& b) {
int tmp = a;
a = b;
b = tmp;
}
void Partition(vector<int>& s, int low, int high, int& pvpoint) {
int j;
int pvitem;
pvitem = s.at(low);
j = low;
for (int i = low + 1; i <= high; i++) {
if (s.at(i) < pvitem) {
j++;
swap(s.at(i), s.at(j));
}
pvpoint = j;
swap(s.at(low), s.at(pvpoint));
}
}
void QuickSort(vector<int>& s, int low, int high) {
int pvpoint;
if (high > low) {
Partition(s, low, high, pvpoint);
QuickSort(s, low, pvpoint - 1);
QuickSort(s, pvpoint + 1, high);
}
}
And each of these main() functions are printing the execution times in SORTED, and RANDOM key arrays.
you can see the result with adding one of these main functions in Visual Studio(C++):
//Sorted key array
int main() {
int s;
for (int i = 1; i < 21; i++) { //Size is from 300 to 6000
s = i * 300;
vector<int> Arr(s);
cout << "N : " << s << "\n";
//Assign Random numbers to each elements
Arr.front() = rand() % Arr.size();
for (int j = 1; j < Arr.size(); j++) { Arr.at(j) = ((737 * Arr.at(j - 1) + 149) % (Arr.size() * 5)); }
sort(Arr.begin(), Arr.end());
//QuickSort(Arr, 0, Arr.size() - 1); <- you can switch using this instead of MergeSort(...) below
for (int i = 0; i < 10; i++) { //print 10 times of execution time
clock_t start, end;
start = clock();
MergeSort(Arr, 0, Arr.size() - 1);
end = clock() - start;
printf("%12.3f ", (double)end * 1000.0 / CLOCKS_PER_SEC);
}
cout << endl;
}
return 0;
}
//Random key array
int main() {
int s;
for (int i = 1; i < 21; i++) {
s = i * 3000;
vector<int> Arr(s);
cout << "N : " << s << "\n";
for (int i = 0; i < 10; i++) {
//Assign Random numbers to each elements
Arr.front() = rand() % Arr.size();
for (int j = 1; j < Arr.size(); j++) { Arr.at(j) = ((737 * Arr.at(j - 1) + 149) % (Arr.size() * 5)); }
//QuickSort(Arr, 0, Arr.size() - 1); <- you can switch using this instead of MergeSort(...) below
clock_t start, end;
start = clock();
MergeSort(Arr, 0, Arr.size() - 1);
end = clock() - start;
printf("%12.3f ", (double)end * 1000.0 / CLOCKS_PER_SEC);
}
cout << endl;
}
return 0;
}
And the THING is, the result is not matching with their time complexity. for example, Merge sort in(RANDOM Array)
size N=3000 prints 20 ms, but size N=60000 prints 1400~1600 ms !! it supposed to print almost 400 ms because Time complexity (Not in worse case) in Quick Sort is O(n.log(n)), isn't it? I want to know what affects to this time and how could I see the printed time that I expected.
You posted the same code in this question: Calculate Execution Times in Sort algorithm and you did not take my answer into account.
Your MergeSort function has a flaw: you duplicate the whole array in merge causing a lot of overhead and quadratic time complexity. This innocent looking definition: vector<int> u(s); defines u as a vector initialized as a copy of s, the full array.
C++ is a very powerful language, often too powerful, littered with traps and pitfalls such as this. It is a very good thing you tried to verify that your program meets the expected performance from the known time complexity of the algorithm. Such a concern is alas too rare.
Here are some guidelines:
For getting execution time:
#include <time.h>
int main()
{
struct timeval stop, start;
int arr[10000];
gettimeofday(&start, NULL);
mergeSort(arr, 0, 9999);
gettimeofday(&stop, NULL);
printf("Time taken for Quick sort is: %ld microseconds\n",
(stop.tv_sec-start.tv_sec)*1000000+stop.tv_usec-start.tv_usec);
}

Time/Space-Complexity method

I got a question to answer with the best complexity we can think about.
We got one sorted array (int) and X value. All we need to do is to find how many places in the array equals the X value.
This is my solution to this situation, as i don't know much about complexity.
All i know is that better methods are without for loops :X
class Question
{
public static int mount (int [] a, int x)
{
int first=0, last=a.length-1, count=0, pointer=0;
boolean found=false, finish=false;
if (x < a[0] || x > a[a.length-1])
return 0;
while (! found) **//Searching any place in the array that equals to x value;**
{
if ( a[(first+last)/2] > x)
last = (first+last)/2;
else if ( a[(first+last)/2] < x)
first = (first+last)/2;
else
{
pointer = (first+last)/2;
count = 1;
found = true; break;
}
if (Math.abs(last-first) == 1)
{
if (a[first] == x)
{
pointer = first;
count = 1;
found = true;
}
else if (a[last] == x)
{
pointer = last;
count = 1;
found = true;
}
else
return 0;
}
if (first == last)
{
if (a[first] == x)
{
pointer = first;
count = 1;
found = true;
}
else
return 0;
}
}
int backPointer=pointer, forwardPointer=pointer;
boolean stop1=false, stop2= false;
while (!finish) **//Counting the number of places the X value is near our pointer.**
{
if (backPointer-1 >= 0)
if (a[backPointer-1] == x)
{
count++;
backPointer--;
}
else
stop1 = true;
if (forwardPointer+1 <= a.length-1)
if (a[forwardPointer+1] == x)
{
count++;
forwardPointer++;
}
else
stop2 = true;
if (stop1 && stop2)
finish=true;
}
return count;
}
public static void main (String [] args)
{
int [] a = {-25,0,5,11,11,99};
System.out.println(mount(a, 11));
}
}
The print command count it right and prints "2".
I just want to know if anyone can think about better complexity for this method.
Moreover, how can i know what is the time/space-complexity of the method?
All i know about time/space-complexity is that for loop is O(n). I don't know how to calculate my method complexity.
Thank a lot!
Editing:
This is the second while loop after changing:
while (!stop1 || !stop2) //Counting the number of places the X value is near our pointer.
{
if (!stop1)
{
if ( a[last] == x )
{
stop1 = true;
count += (last-pointer);
}
else if ( a[(last+forwardPointer)/2] == x )
{
if (last-forwardPointer == 1)
{
stop1 = true;
count += (forwardPointer-pointer);
}
else
forwardPointer = (last + forwardPointer) / 2;
}
else
last = ((last + forwardPointer) / 2) - 1;
}
if (!stop2)
{
if (a[first] == x)
{
stop2 = true;
count += (pointer - first);
}
else if ( a[(first+backPointer)/2] == x )
{
if (backPointer - first == 1)
{
stop2 = true;
count += (pointer-backPointer);
}
else
backPointer = (first + backPointer) / 2;
}
else
first = ((first + backPointer) / 2) + 1;
}
}
What do you think about the changing? I think it would change the time complexity to O(long(n)).
First let's examine your code:
The code could be heavily refactored and cleaned (which would also result in more efficient implementation, yet without improving time or space complexity), but the algorithm itself is pretty good.
What it does is use standard binary search to find an item with the required value, then scans to the back and to the front to find all other occurrences of the value.
In terms of time complexity, the algorithm is O(N). The worst case is when the entire array is the same value and you end up iterating all of it in the 2nd phase (the binary search will only take 1 iteration). Space complexity is O(1). The memory usage (space) is unaffected by growth in input size.
You could improve the worst case time complexity if you keep using binary search on the 2 sub-arrays (back & front) and increase the "match range" logarithmically this way. The time complexity will become O(log(N)). Space complexity will remain O(1) for the same reason as before.
However, the average complexity for a real-world scenario (where the array contains various values) would be very close and might even lean towards your own version.

Big O runtime - indexOf LinkedList/ ArrayList

I have a question considering Big O runtime and the indexOf method within LinkedList, and ArrayList. How can I come up with a Big O runtime assumption and how would it be different in a Linked List as opposed to an Array List?
LinkedList indexOf()
public int indexOf(Object value)
{
int results = -1;
boolean done = false;
Node<E> ref = head.next;
for(int i = 0; i < size && !done; i++)
{
if(value.equals(ref.value))
{
results = i;
done = true;
}
ref = ref.next;
}
return results;
}
ArrayList indexOf()
if (o == null) {
for (int i = 0; i < size; i++)
if (Values[i]==null)
return i;
} else {
for (int i = 0; i < size; i++)
if (o.equals(Values[i]))
return i;
}
return -1;
I apologize if this is a trivial question to some but I am going to need to understand how to come up with a Big O runtime of a method.
In both these implementations you have nothing better to do than go over the list one element at a time and compare it to the value you're looking for. At the worst case, you'd be going through the entire list, giving a complexity of O(n).

Confusion about my QuickSort algorithm & Mergesort algorithm

I am currently conducting empirical studies to evaluate the run-time complexities of the quicksort, and mergesort algorithms. To do this I run a random number generator that stores whatever amount of numbers I specify in a binary file. The ranges of those numbers are from 1-1,000,000.I then run tests of each algorithm starting from 100,000 numbers, incrementing by 50,000 each time, until 1,000,000 numbers are sorted on the last run. So 20 tests each. I have successfully completed each algorithm but my results are kind of puzzingly. This is a graph showing my results.
I understand that quicksort has a worst case of O(n2) time, but typically O(n·lg(n)) time. Mergesort has Θ(n·lg(n)) time.
Also I would like to note that when I started the timer I just used clock() from time.h, and calculated the time elapsed. I started my timer one line of code before I called my sorting function.
What I dont understand is how my graph shows mergesort is always double the time, and reaching triple the time to sort numbers compared to quicksort.
My only thought is that for my mergesort algorithm every time I divide my array in half I use malloc to create a new integer array for each half. Of course this means a large amount of calls are made to malloc considering the number sizes I am sorting.
int* mergeSort(int* nums, int size){
int* left;
int* right;
int middle = size/2;
if(size <= 1)
return nums;
split(nums, size, &left, &right, middle);
//I dont understand why the code below wouldnt work in place of the split()
//when i run it, in main, nothing gets printed out. I guess i lose my pointer to the beginning of my array.
//left = nums;
//right = nums+middle;
left = mergeSort(left, middle);
right = mergeSort(right, size - middle);
merge(nums,left,right,middle,size - middle);
free(left);
free(right);
return nums;
}
void split(int* nums, int size, int** left, int** right, int middle){
int *lft = (int*) malloc ((sizeof(int) * middle));
int *rght = (int*) malloc ((sizeof(int) * size - middle));
int mid = middle;
int upMid = size - middle;
int i;
for(i=0; i < mid; i++)
lft[i] = nums[i];
for(i=0; i < upMid; i++)
rght[i] = nums[i+middle];
*left = lft;
*right = rght;
}
void merge(int* num, int* left, int* right, int sizeLeft, int sizeRight){
int i,j,k,n;
i=j=k=0;
n=sizeLeft + sizeRight;
while(k < n){
if(i< sizeLeft){
if(j<sizeRight){
insert(num,left,right,&i,&j,&k);
}
else{
append(num, left, sizeLeft, &i, &k);
}
}
else{
append(num,right,sizeRight,&j,&k);
}
}
}
void insert(int* num, int* left, int* right, int* i, int* j, int* k){
/*int i,j,k,n;*/
if(left[*i]<right[*j]){
num[*k] = left[*i];
(*i)++;
}
else{
num[*k] = right[*j];
(*j)++;
}
(*k)++;
}
void append(int* num, int* half, int sizeHalf, int* i, int* k){
while(*i < sizeHalf){
num[*k]= half[*i];
(*i)++; (*k)++;
}
}
I would greatly appreciate any feedback on this question of mine, and any advice on maybe making my mergesort function more efficient. Thanks!!
I have implemented a merge sort algorithm, you can have a look. I malloc a bak array at the beginning of mergeSort and every merge use the it afterwards.
#include <string>
#include <stdlib.h>
void _mergeSort(int *array, int *bakArray, int len) ;
void mergeSort(int *array, int len)
{
int *bak = (int *)malloc(sizeof(int)*len) ;
_mergeSort(array, bak, len) ;
free(bak) ;
}
void _mergeSort(int *array, int *bakArray, int len)
{
if (len >= 2) {
int leftLen = len/2 ;
_mergeSort(array, bakArray, leftLen) ;
_mergeSort(array+leftLen, bakArray, len-leftLen) ;
int *pa = array ;
int *pb = array+leftLen ;
int aIndex = 0 ;
int bIndex = 0 ;
while (aIndex < leftLen && bIndex < len-leftLen) {
int a = pa[aIndex] ;
int b = pb[bIndex] ;
if (a < b) {
bakArray[aIndex+bIndex] = a ;
++aIndex ;
} else if (a == b) {
bakArray[aIndex+bIndex] = a ;
bakArray[aIndex+bIndex+1] = a ;
++aIndex ;
++bIndex ;
} else {
bakArray[aIndex+bIndex] = b ;
++bIndex ;
}
}
if (aIndex < leftLen) {
memcpy(bakArray+aIndex+bIndex, pa+aIndex, sizeof(int)*(leftLen-aIndex)) ;
} else if (bIndex < len-leftLen) {
memcpy(bakArray+aIndex+bIndex, pb+bIndex, sizeof(int)*(len-leftLen-bIndex)) ;
}
memcpy(array, bakArray, sizeof(int)*len) ;
}
}
static const int MaxArraySize = 100 ;
int main()
{
srand(time(NULL)) ;
int array[MaxArraySize] ;
for (int i = 0 ; i < MaxArraySize; ++i) {
array[i] = rand() % 10000 ;
}
mergeSort(array, MaxArraySize) ;
for (int i = 0 ; i < MaxArraySize; ++i) {
printf("%d ", array[i]) ;
}
printf("\n") ;
return 0 ;
}

Omaha Hi Hand Evaluator

Currently I'm trying to port Keith Rule's Texas Holdem Hand Evaluator to Omaha Hi:
Texas Holdem Evaluator and Analysis
More Analysis Part1
More Analysis Part 2
After thinking more about the algorithm, I found a solution which gives me the right percentages for the hands and everything is fine..
But it's really really slow. How can I speed things up?
As the only thing I do right now is to look-up a normal five card hands, a LUT might be right for me. Anyone integrated one before?
static void Main(string[] args)
{
long count = 0;
double player1win = 0.0, player2win=0.0;
ulong player1 = Hand.ParseHand("Ad Kd As Ks");
ulong player2 = Hand.ParseHand("Th 5c 2c 7d");
foreach (ulong board in Hand.Hands(0, player1 | player2, 5))
{
uint maxplayer1value = 0, maxplayer2value = 0;
foreach (ulong boardcards in Hand.Hands(0, ulong.MaxValue ^ board, 3))
{
foreach (ulong player1hand in Hand.Hands(0Ul, ulong.MaxValue ^ player1, 2))
{
uint player1value = Hand.Evaluate(player1hand | boardcards, 5);
if (player1value > maxplayer1value) maxplayer1value = player1value;
}
}
foreach (ulong boardcards in Hand.Hands(0, ulong.MaxValue ^ board, 3))
{
foreach (ulong player2hand in Hand.Hands(0UL, ulong.MaxValue ^ player2, 2))
{
uint player2value = Hand.Evaluate(player2hand | boardcards, 5);
if (player2value > maxplayer2value) maxplayer2value = player2value;
}
}
if (maxplayer1value > maxplayer2value)
{
player1win += 1.0;
}
else if (maxplayer2value > maxplayer1value)
{
player2win += 1.0;
}
else
{
player1win += 0.5;
player2win += 0.5;
}
count++;
}
Console.WriteLine("Player1: {0:0.0000} Player2: {1:0.0000} Count: {2}", player1win / count * 100, player2win / count * 100, count);
Console.ReadLine();
}
Looks like you're trying to create equity calculator. I've done this as well, but not for Omaha (Texas Hold'em instead). With then players to evaluate, I've got about ~200K hands per second, which gives accurate result enough in no time. If there only two players to
evaluate, I can get up to 4 million evaluations per second.
I used bitmasks for hands. One 64-bit integer to represent card, hand or entire board. You only need actually 52 of it, obviously. By using bitwise-operators, things get going rather quickly. Here's a quick sample from my project (in C++ tho). It's using 2 + 2 evaluator
for fast look-ups:
while (trial < trials) {
/** I use here a linked list over the hand-distributions (players).
* This is kind of natural as well, as circle is the basic
* shape of poker.
*/
pDist = pFirstDist;
unsigned __int64 usedCards = _deadCards;
bool collision;
/** Here, we choose random distributions for the comparison.
* There is a chance, that two separate distributions has
* the same card being picked-up. In that case, we have a collision,
* so do the choosing again.
*/
do {
pDist->Choose(usedCards, collision);
/** If there is only one hand in the distribution (unary),
* there is no need to check over collision, since it's been
* already done in the phase building them (distributions).
*/
if (pDist->_isUnary)
collision = false;
pDist = pDist->_pNext;
} while (pDist != pFirstDist && !collision);
if (collision) {
/** Oops! Collision occurred! Take the next player (hand-
* distribution and do this all over again.
*
*/
pFirstDist = pDist->_pNext;
continue;
}
unsigned __int64 board = 0;
/** Pick a board from the hashed ones, until it's unique compared to
* the distributions.
*
*/
do {
if (count == 1) {
board = boards[0];
collision = false;
} else {
board = boards[Random()];
collision = (board & usedCards) != 0;
}
} while (collision);
board |= _boardCards;
int best = 0, s = 1;
do {
pDist->_currentHand |= board;
unsigned long i, l = static_cast<unsigned long>(pDist->_currentHand >> 32);
int p;
bool f = false;
/** My solution to find out the set bits.
* Since I'm working on a 32-bit environment, the "64-bit"
* variable needs to be split in to parts.
*/
if (_BitScanForward(&i, l)) {
p = _evaluator->_handRanks[53 + i + 32]; // Initial entry to the 2 + 2 evaluator hash.
l &= ~(static_cast<unsigned long>(1) << i);
f = true;
}
if (f)
while (_BitScanForward(&i, l)) {
l &= ~(static_cast<unsigned long>(1) << i);
p = _evaluator->_handRanks[p + i + 32];
}
l = static_cast<unsigned long>(pDist->_currentHand & 0xffffffff);
if (!f) {
_BitScanForward(&i, l);
p = _evaluator->_handRanks[53 + i];
l &= ~(static_cast<unsigned long>(1) << i);
}
while (_BitScanForward(&i, l)) {
l &= ~(static_cast<unsigned long>(1) <<_handRanks[p + i];
}
pDist->_rank = p;
/** Keep the statistics up. Please do remember, that
* equity consist of ties as well, so it's not a percentual
* chance of winning.
*/
if (p > best) {
pWinner = pDist;
s = 1;
best = p;
} else if (p == best)
++s;
pDist = pDist->_pNext;
} while (pDist != pFirstDist);
if (s > 1) {
for (unsigned int i = 0; i _rank == best) {
_handDistributions[i]->_ties += 1.0f / s;
_handDistributions[i]->_equity += 1.0f / s;
}
} else {
++pWinner->_wins;
++pWinner->_equity;
}
++trial;
pFirstDist = pDist->_pNext;
}
Please refer to the 2 + 2 evaluator, which is quite easy to adapt in your own needs.
This might help:
An example of a ready made Objective-C (and Java) Texas Hold'em 7- and 5-card evaluator can be found here and further explained here. It "adds" up hands to generate an index that sufficiently characterises the hand for determining rank.
All feedback welcome at the e-mail address found therein