Here's a very horrible sorting algorithm that only works on integers ranging from 0 to size. I wouldn't use such an algorithm on a large set of data or one with large numbers in it because it would require so much memory. That consider, wouldn't this algorithm technically run in O(n) time?
Thanks
#include <iostream>
#define size 33
int main(){
int b[size];
int a[size] = {1,6,32,9,3,7,4,22,5,2,1,0};
for (int n=0; n<size; n++) b[n] = 0;
for (int n=0; n<size; n++){
if (size <= a[n]) throw std::logic_error("error");
b[a[n]]++;
}
int x = 0;
for (int n=0; n<size; n++){
for (int t=0; t<b[n]; t++){
a[x] = n;
x++;
}
}
for (int n=0; n<size; n++) printf("%d ",a[n]);
}
You're showing a variation on radix sort. Along with bucket sort, these algorithms are prime examples of sorting not based on comparison, which can offer better complexity than O(n logn).
Do note, however, that your implementation is actually O(n + size).
It is O(n). More specifically it is O(n+k) where n is the number of elements in input array and k is the range of input in your case size. You are just keeping counts of all elements which occur in the array. And then you just store them in increasing order as many times as they occur in the original array. This algorithm is called count sort.
Related
Given some array of numbers i.e. [5,11,13,26,2,5,1,9,...]
What is the time complexity of these loops? The first loop is O(n), but what is the second loop? It iterates the number of times specified at each index in the array.
for (int i = 0; i < nums.size(); i++) {
for (int j = 0; j < nums[i]; j++) {
// ...
}
}
This loop has time complexity O(N*M) (using * to denote multiplication).
N is the number of items in your list, M is either the average value for your numbers, or the maximum possible value. Both would yield the same order, so use whichever is easier.
That arises because the number of times ... runs is proportional to both N and M. It also assumes ... to be constant complexity. If not you need to multiply by the complexity of ....
It is O(sum(nums[i]) * nums.size())
My solution :
#include <bits/stdc++.h>
int main() {
int n;//Size of array
std::cin>>n;
std::vector<long long>vec_int;
int temp = n;
while(n--){
long long k ;
std::cin>>k;
vec_int.push_back(k);
}
n = temp;
int num = 0;
for(int i = 0 ; i < n-1 ; i++){
for(int j = i+1; j<n; j++){
if(i<j && i+j == vec_int[i]+vec_int[j])
num++;
}
}
std::cout<<num;
return 0;
}
I am scanning the array which takes about O(n^2) time. On very large arrays the time limit for the question exceeds the 2s duration. I tried sorting the array but didn't get too far. How can I speed this up? Is it possible to do this in O(n) time complexity.
Consider redefinition of your problem. The expression:
i+j == vec_int[i]+vec_int[j]
is algebraically equivalent to:
vec_int[i] - i == -(vec_int[j] - j)
So define:
a[i] = vec_int[i] - i
And now the question is to count how many times a[i] == -a[j].
This can be tested in O(n). Use unordered_map m to count how many times each negative value is present in a. Then for each positive value a[i] will be paired with m[-a[i]] negative values. Also count number of zeroes in a and compute number of pairs between those.
so I have a task. I've been given n numbers and m intervals and I need to figure out how many numbers are in the m i-th interval. I've written some code with a complexity of O(n*m), though I need to optimize it more. Any help?
Code:
#include <bits/stdc++.h>
using namespace std;
int main()
{
cin.tie(0);
ios_base::sync_with_stdio(0);
cout.tie(0);
int n,m,temp,temp1;
vector <pair<int, int>> uogienes;
vector <int> erskeciai;
cin >> n >> m;
for (int i = 0; i< n; i++){
cin>>temp;
erskeciai.push_back(temp);
}
temp = 0;
for (int i = 0; i<m; i++){
cin>> temp >> temp1;
uogienes.push_back(make_pair(temp, temp1));
}
for(int i = 0; i<m; i++){
temp=0;
for(int h = 0; h<n; h++){
if(uogienes[i].first <= erskeciai[h] && uogienes[i].second >= erskeciai[h]){
temp++;
}
}
cout << temp << "\n";
}
return 0;
}
As DAle already noted.
You can first sort the n numbers. A good algorithm, like merge or heap sort, will give you a complexity O(n*log(n)).
After that you need to use search algorithm for both your 'first' and 'second' parts of each interval. Depending on the algorithm, the complexity should be around O(log(n)) - std::lower_bound has complexity of O(log(n)) when working on sorted data, so its good enough. Or that will be O(m*log(n)) for all intervals.
Comparing the result of the search will give you the amount of numbers in each interval.
In total you'll have around O((m+n)*log(n)).
A goal of mine is to reduce my O(n^2) algorithms into O(n), as it's a common algorithm in my Array2D class. Array2D holds a multidimensional array of type T. A common issue I see is using doubly-nested for loops to traverse through an array, which is slow depending on the size.
As you can see, I reduced my doubly-nested for loops into a single for loop here. It's running fine when I execute it. Speed has surely improved. Is there any other way to improve the speed of this member function? I'm hoping to use this algorithm as a model for my other member functions that have similar operations on multidimensional arrays.
/// <summary>
/// Fills all items within the array with a value.
/// </summary>
/// <param name="ob">The object to insert.</param>
void fill(const T &ob)
{
if (m_array == NULL)
return;
//for (int y = 0; y < m_height; y++)
//{
// for (int x = 0; x < m_width; x++)
// {
// get(x, y) = ob;
// }
//}
int size = m_width * m_height;
int y = 0;
int x = 0;
for (int i = 0; i < size; i++)
{
get(x, y) = ob;
x++;
if (x >= m_width)
{
x = 0;
y++;
}
}
}
Make sure things are contiguous in memory as cache behavior is likely to dominate the run-time of any code which performs only simple operations.
For instance, don't use this:
int* a[10];
for(int i=0;i<10;i++)
a[i] = new int[10];
//Also not this
std::vector< std::vector<int> > a(std::vector<int>(10),10);
Use this:
int a[100];
//or
std::vector<int> a(100);
Now, if you need 2D access use:
for(int y=0;y<HEIGHT;y++)
for(int x=0;x<WIDTH;x++)
a[y*WIDTH+x];
Use 1D accesses for tight loops, whole-array operations which don't rely on knowledge of neighbours, or for situations where you need to store indices:
for(int i=0;i<HEIGHT*WIDTH;i++)
a[i];
Note that in the above two loops the number of items touched is HEIGHT*WIDTH in both cases. Though it may appear that one has a time complexity of O(N^2) and the other O(n), it should be obvious that the net amount of work done is HEIGHT*WIDTH in both cases. It is better to think of N as the total number of items touched by an operation, rather than a property of the way in which they are touched.
Sometimes you can compute Big O by counting loops, but not always.
for (int m = 0; m < M; m++)
{
for (int n = 0; n < N; n++)
{
doStuff();
}
}
Big O is a measure of "How many times is doStuff executed?" With the nested loops above it is executed MxN times.
If we flatten it to 1 dimension
for (int i = 0; i < M * N; i++)
{
doStuff();
}
We now have one loop that executes MxN times. One loop. No improvement.
If we unroll the loop or play games with something like Duff's device
for (int i = 0; i < M * N; i += N)
{
doStuff(); // 0
doStuff(); // 1
....
doStuff(); // N-1
}
We still have MxN calls to doStuff. Some days you just can't win with Big O. If you must call doStuff on every element in an array, no matter how many dimensions, you cannot reduce Big O. But if you can find a smarter algorithm that allows you to avoid calls to doStuff... That's what you are looking for.
For Big O, anyway. Sometimes you'll find stuff that has an as-bad-or-worse Big O yet it outperforms. One of the classic examples of this is std::vector vs std::list. Due to caching and prediction in a modern CPU, std::vector scores a victory that slavish obedience to Big O would miss.
Side note (Because I regularly smurf this up myself) O(n) means if you double n, you double the work. This is why O(n) is the same as O(1,000,000 n). O(n2) means if you double n you do 22 times the work. If you are ever puzzled by an algorithm, drop a counter into the operation you're concerned with and do a batch of test runs with various Ns. Then check the relationship between the counters at those Ns.
Given a number 'n', I want to return a sorted array of n^2 numbers containing all the values of k1*k2 where k1 and k2 can range from 1 to n.
For example for n=2 it would return : {1,2,2,4}.(the number are basically 1*1,1*2,2*1,2*2).
and for n=3 it would return : {1,2,2,3,3,4,6,6,9}.
(the numbers being : 1*1, 2*1, 1*2, 2*2, 3*1, 1*3, 3*2, 2*3, 3*3)
I tried it using sort function from c++ standard library, but I was wondering if it could be further optimized.
Well, first of all, you get n^2 entries, the largest of which will be n^2, and of the possible value range, only a tiny amount of values is used for large n. So, I'd suggest a counting approach:
Initialize an array counts[] of size n^2 with zeros.
Iterate through your array of values values[], and do counts[values[i]-1]++.
Reinitialize the values array by iterating through the counts array, dropping as many values of i+1 into the values array as counts[i] gives you.
That's all. It's O(n^2), so you'll hardly find a more performant solution.
vector<int> count(n*n+1);
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j)
++count[i*j];
for (int i = 1; i <= n*n; ++i)
for (int j = 0; j < count[i]; ++j)
cout << i << " ";
This is in essence the O(n*n) solution as described in cmaster's answer.