O(n2) ,how to optimized two loop? - c++

I want to compare the elements in array whether there is a similar element is present or not?
I am using two loops and it gives TLE can anyone help me in this?
code:-
int main() {
int arr=[1, 2, 3, 3, 4, 5, 6, 5, 6]
int max=0;
int k;
int c=0;
for (int i=0;i<n;i++) {
for (int j= i+1;j<n;j++) {
if (a[i]==a[j]) {
c++;
if (k>max) {
max= k;
}
}
}
}
}
how to optimize this?

The std::sort function time complexity is nlog(n) (According to Wikipedia, it's Intro Sort)
So one approach for your question is to:
Sort the array (It's better to use std::vector instead of C-Style array)
Compare every element with the next one (Which is going to be O(n))
If the two elements are equal, so show it or add it to a std::set to use it later
Here is my implementation for the algorithm that I mentioned before:
#include <vector>
#include <iostream>
#include <set>
#include <algorithm>
int main() {
std::vector<int> data{1, 2, 3, 4, 6, 2, 7, 1, 5, 4};
std::set<int> duplicatedValues;
std::sort(data.begin(), data.end());
for (int i{0}; i < data.size()-1; ++i)
if (data[i] == data[i+1])
duplicatedValues.insert(data[i]);
for (int value : duplicatedValues)
std::cout << value << " ";
std::cout << std::endl;
}
So time complexity for code above is going to be nlog(n)
Note: I should add that if you have n data and all of them are between range of 0 to n-1, We have O(n) for that, take a look at here.
Second Approach: (By using std::unordered_map)
(It takes a little shorter time than the previous approach, but roughly 2 times more memory!)
We can add every element in a dictionary and if we had that same data again in our array, increase its number in dictionary; and finally, show elements that have number more than 1.
#include <iostream>
#include <unordered_map>
#include <vector>
#include <stdlib.h>
int main() {
std::vector<int> data{1, 2, 8, 2, 5, 4, 1, 9, 2, 3, 8};
std::unordered_map<int, int> duplicatedData;
for (int i{0}; i < data.size(); ++i) {
if (duplicatedData.find(data[i]) == duplicatedData.end()) {
duplicatedData[data[i]] = 1;
}
else {
duplicatedData[data[i]] += 1;
}
}
for (auto x : duplicatedData) {
if (x.second > 1) {
std::cout << x.first << " ";
}
}
std::cout << std::endl;
}
EDIT: I recorded a video for this, with performance measurement in here

i will do it like
#include <algorithm>
#include <vector>
#include <execution>
#include <atomic>
#include <iostream>
int main()
{
int arr[] = {1,2,3,5,6};
std::atomic<bool> com_tr = false;
std::for_each(std::execution::par, &arr[0], (&arr[0] + sizeof(arr)/sizeof(int)),
[&](auto a)
{ com_tr = std::count(&arr[0], (&arr[0] + sizeof(arr)/sizeof(int)), a) == 1 ? (bool)com_tr : !false; });
if (com_tr) std::cout << "have one " << std::endl;
}
compiled with c++ -std=c++17 -O3 '/home/alex/etr/b.cpp' -o t -ltbb
but if it's really all you need to accomplish better would be std::any_of()

Related

Finding Duplicates in an array using a Set in C++

I am currently practicing for coding interviews and am working on a function that takes in an array and the size of that array and prints out which numbers in it are duplicates. I have gotten this to work using the two for loop method but want an optimized solution using sets. Snippet of the code I have is below,
#include <iostream>
#include <set>
using namespace std;
void FindDuplicate(int integers[], int n){
set<int>setInt;
for(int i = 0; i < n; i++){
//if this num is not in the set then it is not a duplicate
if(setInt.find(integers[i]) != setInt.end()){
setInt.insert({integers[i]});
}
else
cout << integers[i] << " is a duplicate";
}
}
int main() {
int integers [] = {1,2,2,3,3};
int n = sizeof(integers)/sizeof(integers[0]);
FindDuplicate(integers, n);
}
Any helpful advice is appreciated, thanks
I think your comparison is not needed, insert do it for you:
https://en.cppreference.com/w/cpp/container/set/insert
Returns a pair consisting of an iterator to the inserted element (or
to the element that prevented the insertion) and a bool value set to
true if the insertion took place.
Just insert element and check what insert function returns (false on second element of pair in case of duplication) :)
my solution proposal is :
count the frequencies of each element (algo for frequencies are explained here frequency
display elements with frequency more than 1 (it is a duplicate)
In each operation, you do not use imbricated loops.
#include <iostream>
#include <unordered_map>
using namespace std;
void FindDuplicate(int integers[], int n)
{
unordered_map<int, int> mp;
// Traverse through array elements and
// count frequencies
for (int i = 0; i < n; i++)
{
mp[integers[i]]++;
}
cout << "The repeating elements are : " << endl;
for (int i = 0; i < n; i++) {
if (mp[integers[i]] > 1)
{
cout << integers[i] << endl;
mp[integers[i]] = -1;
}
}
}
int main()
{
int integers [] = {1,1,0,0,2,2,3,3,3,6,7,7,8};
int n = sizeof(integers)/sizeof(integers[0]);
FindDuplicate(integers, n);
}
This is my feedback:
#include <iostream>
#include <vector>
#include <set>
// dont' do this, in big projects it's not done (nameclash issues)
// using namespace std;
// pass vector by const reference you're not supposed to change the input
// the reference will prevent data from being copied.
// naming is important, do you want to find one duplicate or more...
// renamed to FindDuplicates because you want them all
void FindDuplicates(const std::vector<int>& input)
{
std::set<int> my_set;
// don't use index based for loops if you don't have to
// range based for loops are more safe
// const auto is more refactorable then const int
for (const auto value : input)
{
//if (!my_set.contains(value)) C++ 20 syntax
if (my_set.find(value) == my_set.end())
{
my_set.insert(value);
}
else
{
std::cout << "Array has a duplicate value : " << value << "\n";
}
}
}
int main()
{
// int integers[] = { 1,2,2,3,3 }; avoid "C" style arrays they're a **** to pass around safely
// int n = sizeof(integers) / sizeof(integers[0]); std::vector (or std::array) have size() methods
std::vector input{ 1,2,2,3,3 };
FindDuplicates(input);
}
You do not need to use a set.
To find the duplicates:
Sort array with numbers
Iterate over the array (start with second element) and copy elements where previous element equals
current element into a new vector "duplicates"
(Optional) use unique on the "duplicates" if you like to know which number is a duplicate and do not care if it is 2, 3 or 4 times in the numbers array
Example Implementation:
#include <algorithm>
#include <iostream>
#include <vector>
void
printVector (std::vector<int> const &numbers)
{
for (auto const &number : numbers)
{
std::cout << number << ' ';
}
std::cout << std::endl;
}
int
main ()
{
auto numbers = std::vector<int>{ 1, 2, 2, 42, 42, 42, 3, 3, 42, 42, 1, 2, 3, 4, 5, 6, 7, 7 };
std::sort (numbers.begin (), numbers.end ());
auto duplicates = std::vector<int>{};
std::for_each (numbers.begin () + 1, numbers.end (), [prevElement = numbers.begin (), &duplicates] (int currentElement) mutable {
if (currentElement == *prevElement)
{
duplicates.push_back (currentElement);
}
prevElement++;
});
duplicates.erase (std::unique (duplicates.begin (), duplicates.end ()), duplicates.end ());
printVector (duplicates);
}
edit:
If you have no problem with using more memory and more calculations but like it more expressive:
Sort numbers
Create a new array with unique numbers "uniqueNumbers"
Use "set_difference" to calculate (numbers-uniqueNumbers) which leads to an new array with all the duplicates
(Optional) use unique on the "duplicates" if you like to know which number is a duplicate and do not care if it is 2, 3 or 4 times in the numbers array
Example Implementation:
#include <algorithm>
#include <iostream>
#include <vector>
void
printVector (std::vector<int> const &numbers)
{
for (auto const &number : numbers)
{
std::cout << number << ' ';
}
std::cout << std::endl;
}
int
main ()
{
auto numbers = std::vector<int>{ 2, 2, 42, 42, 42, 3, 3, 42, 42, 1, 2, 3, 4, 5, 6, 7, 7 };
std::sort (numbers.begin (), numbers.end ());
auto uniqueNumbers = std::vector<int>{};
std::unique_copy (numbers.begin (), numbers.end (), std::back_inserter (uniqueNumbers));
auto duplicates = std::vector<int>{};
std::set_difference (numbers.begin (), numbers.end (), uniqueNumbers.begin (), uniqueNumbers.end (), std::back_inserter (duplicates));
std::cout << "duplicate elements: ";
printVector (duplicates);
std::cout << "unique duplicate elements: ";
printVector ({ duplicates.begin (), std::unique (duplicates.begin (), duplicates.end ()) });
}
here's a quick solution use an array of size N (try a big number)
and whenever a number is added into the other array on the large array add 1 to the position like:
array_of_repeated[user_input]++;
so if the program asks how many times (for example) number 234 was repeated?
std::cout<<array_of_repeated[requested_number]<<std::endl;
so in this way you wont spend time looking for a number inside the other list

Getting index of unique elements in a vector

Is there any STL/Boost function in C++ that allows me to find the indices of all unique elements in a vector?
I have seen many solutons to find unique elements, but I need their index.
vector<int> v = { 1,1,1, 2,2,2,2, 3, 3, ,4,5,5,5,5,5,5 };// already sorted
Either I need first index of unique elemnt
vector<int> unique_index={0,3,7,9,10};
or I need last index of unique elements
vector<int> unique_index={2,6,8,9,15};
A simple way (aside from just keeping track of what the last element was) is to use a std::set to test if the current element is unique in the elements of the vector -- seen so far, and populate your unique indexes as you go. This provides a single pass through to collect the indexes where the first unique element is seen, e.g.
#include <iostream>
#include <vector>
#include <set>
int main (void) {
std::vector<int> v = { 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 4, 5, 5, 5, 5, 5, 5 },
uniqueidx{};
std::set<int> s{};
for (size_t i = 0; i < v.size(); i++)
if (s.insert(v[i]).second)
uniqueidx.push_back(i);
for (const auto i : uniqueidx)
std::cout << i << '\n';
}
Example Use/Output
$ ./bin/set_index_of_unique_in_vector
0
3
7
10
11
(note: the last two values are 10 and 11, not 9 and 10 -- you are missing a value in your vector initialization, e.g. 3, ,4)
If you just wanted a simple-old loop to do the same thing, you could use:
#include <iostream>
#include <vector>
int main (void) {
std::vector<int> v = { 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 4, 5, 5, 5, 5, 5, 5 },
uniqueidx{};
for (size_t i = 0; i < v.size(); i++)
if (!i || v[i-1] != v[i])
uniqueidx.push_back(i);
for (const auto i : uniqueidx)
std::cout << i << '\n';
}
(same output)
The benefit of the approach with std::set is you leave the determination of uniqueness up to std::set, with the simple loop -- it's up to you....
Look things over and let me know if you have questions.
Similar to David's answer of using std::set, you could also use std::map with its member function try_emplace(key, value):
#include <iostream>
#include <vector>
#include <map>
int main (void) {
std::vector<int> v = { 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 4, 5, 5, 5, 5, 5, 5 };
std::map<int, int> m;
for (size_t i = 0; i < v.size(); i++)
{
// `i` is only entered if the `m[v[i]]` isn't filled yet.
m.try_emplace(v[i], i);
}
for (auto [valueFromV, indexFromV] : m)
{
std::cout << indexFromV << '\n';
}
}
you can change this code and using int instead of a string.
#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <iterator>
#include <algorithm>
std::map<std::string, int> get_unique_indices(const std::vector<std::string>& products) {
std::vector<std::string> tempProducts(products);
std::map<std::string, int> result;
std::sort(std::begin(tempProducts), std::end(tempProducts));
for (auto it1 = std::begin(tempProducts), it2 = it1; it1 != std::end(tempProducts); it1 = it2) {
int duplication = 0;
for (; it2 != std::end(tempProducts) && (*it2 == *it1); ++it2) {
duplication++;
}
if (duplication == 1) {
result.insert({ *it1, std::find(std::begin(products), std::end(products), *it1) - std::begin(products) });
}
}
return result;
}
int main()
{
using namespace std;
vector<string> products = { "apple", "orange", "lemon", "apple", "kivi", "orange", "kivi", "melon"};
auto result = get_unique_indices(products);
for (const auto& [key, value] : result) {
std::cout << key << " " << value << std::endl;
}
return 0;
}
At first, I create another vector to save the original data. (Cause the position of the item will change in the sort method).
then I use two iterators to find duplicate items and count them. the result will be saved in a map instance.
this code must compile with c++17 onwards.

c++ return all of the elements that appear n times in a vector as a vector

Goal: Return all elements in vector A that appear N times and put results in vector B.
Expected Result:
--Begin With---
Vector A=(10,20,30,30,40,50,100,50,20,100,10,10,200,300)
Do some code to return the name of elements that appear in Vector A
when N=3
Result should be Vector B=(10) //because only 10 is in vector A N=3 times.
My Attempt:
I got the counts of all the elements placed into another vector but I don't have the part that can give back all of the elements that appear N times. I'm very flexible with how it can be done if it means a speed increase.
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include <iterator> // std::back_inserter
#include <algorithm> // std::copy
int main()
{
std::vector<int> v{ 1, 2, 3, 4, 4, 3, 7, 8, 9, 10 };
std::vector<std::pair<int, int> > shows;
int target1;
int num_items1;
int size = static_cast<int>(v.size());
for(int x=0; x<size; x++)
{
target1 = v[x];
num_items1 = std::count(v.begin(), v.end(), target1);
shows.push_back(std::make_pair(target1, num_items1));
std::cout << "number: " << target1 << " count: " << num_items1 << '\n';
}
}
ACCEPTED SOLUTION TO QUESTION
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include <iterator> // std::back_inserter
#include <algorithm> // std::copy
#include <set>
#include <map>
using namespace std;
int main()
{
std::vector<int> v{ 1, 2, 3, 4, 4, 3, 7, 8, 9, 10 };
std::vector<int> shows;
std::map<int, int> freqOfv;
for(const auto& i: v)
{
freqOfv[i]++;
}
std::set<int> s(v.begin(), v.end());
int N = 2; //Can be read from stdin as well...
for ( auto it = s.begin(); it != s.end(); it++ )
{
if(freqOfv[*it] ==N)
{
shows.push_back(*it);
}
}
for (std::vector<int>::const_iterator i = shows.begin(); i != shows.end(); ++i)
{
std::cout << *i << ' ';
}
return 0;
}
As suggested in the comments, std::map will simplify the code:
int main()
{
std::vector<int> v{ 1, 2, 3, 4, 4, 3, 7, 8, 9, 10 };
std::map<int, int> freqOfv;
for(const auto& i: v)
freqOfv[i]++;
int N = 2; //Can be read from stdin as well...
for(const auto& i: freqOfv)
{
if(N == i.second)
std::cout << "The value " << i.first << " occurs " << N << " times." << std::endl;
}
}
This produces the following output:
The value 3 occurs 2 times.
The value 4 occurs 2 times.
Of course, you need #include <map> at the beginning to use maps in your code.

Filling a new vector by sampling without replacement from an old one

Is there a good and efficient algorithm in C++ for sampling without replacement that could easily be applied to the following function?
It takes two vectors, new and old, and fills the latter in a loop by repeatedly sampling from the former (rng.i0 is a random number generator function that I use to return a random integer between 0 and given value).
void diluationexpansionstep(std::vector<long> &oldpopulation,
std::vector<long> &newpopulation,
long newpopsize)
{
for (int i = 1; i <= newpopsize;i++) {
int index_a = rng.i0(oldpopulation.size());
newpopulation.push_back(oldpopulation[index_a]);
}
}
Update::
Thank you for helpful responses. Because i want to use my own RNG rather than the inbuilt one in C++ i ended constructing the following Fisher Yates based function where rng.i0 is a function that returns a random intiger between 0 and integer parameter.
void FisherYatesShuffle(vector<long> &indices){
for (int k = 0; k < indices.size(); k++) {
int r = k + rng.i0(indices.size()-k);
swap(indices[k], indices[r]);
}
}
void diluationexpansionstep(std::vector<long> &oldpopulation,
std::vector<long> &newpopulation,
long newpopsize){
vector<long> indices(oldpopulation.size());
std::iota(std::begin(indices),std::end(indices),0);
FisherYatesShuffle(indices);
for (int i = 0; i <= newpopsize-1;i++){
newpopulation.push_back(oldpopulation[indices[i]]);
}
}
As far as i can tell this work accurately and reasonably quickly.
A "good and efficient" algorithm for minimizing developer time and maximizing correctness, using std::random_shuffle from <algorithm>:
#include <algorithm>
#include <cassert>
#include <iostream>
#include <vector>
using namespace std;
vector<long> random_sample_without_replacement(const vector<long>& source, int newpopsize)
{
assert(newpopsize >= 0 && newpopsize <= source.size());
auto result { source };
std::random_shuffle(result.begin(), result.end());
result.resize(newpopsize);
return result;
}
int main() {
vector<long> test { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
auto result { random_sample_without_replacement(test, 5) };
for (auto& e : result) cout << e << " ";
cout << endl;
return 0;
}
Working example here.

C++ check how many same elements in a row are in a vector

I have a big vector with 24.000 elements like :
(1,1,1,1,3,3,3,3,3,3,5,5,5,...etc)
and I want to check how many same elements are in a row like:
4-6-3..etc
I use this code :
static int counter=1;
vector<int>numbers;
for(int n=0;n<numbers.size()-1;n++)
{
if(numbers[n]==numbers[n+1])
{
counter++;
}
else if(numbers[n]!=numbers[n+1])
{
cout<<counter<<endl;
counter=1;
}
}
is there any algorithm that does the same faster;
#rhalbersma basically gave you the right answer. As an addendum, in case you want to rewrite your algorithm in a more standard fashion:
#include <algorithm>
#include <vector>
#include <iterator>
#include <functional>
#include <iostream>
int main()
{
std::vector<int> v { 1, 1, 2, 3, 3, 5, 5, 5 }; // or whatever...
auto i = begin(v);
while (i != end(v))
{
auto j = adjacent_find(i, end(v), std::not_equal_to<int>());
if (j == end(v)) { std::cout << distance(i, j); break; }
std::cout << distance(i, j) + 1 << std::endl;
i = next(j);
}
}
Here is a live example.
Also, when the vector is sorted, this will give you better best-case complexity:
#include <algorithm>
#include <vector>
#include <iterator>
#include <iostream>
int main()
{
std::vector<int> v { 1, 1, 2, 3, 3, 5, 5, 5 }; // must be sorted...
auto i = begin(v);
while (i != end(v))
{
auto ub = upper_bound(i, end(v), *i);
std::cout << distance(i, ub) << std::endl;
i = ub;
}
}
Here is a live example.
Your algorithm is O(N) in time, and that seems pretty optimal to me since you have to visit every unique element for comparison. You might still shave off a few cycles here and there e.g. by eliminating the condition inside the else() or by turning on some compiler settings, but algorithmically you are in good shape.
If the input were already sorted, you could do a series of binary searches. That would give you O(N lg N) worst-case complexity but the average case might be considerably lower depending on the average length of equal element sequences.
BTW, as #AndyProwl shows in his answer: the Standard Library is really awesome to do even this kind of low-level algorithmic stuff. The adjacent_find and upper_bound algorithms have well-documented complexities and the iterator conventions will guard you for edge cases that are present in your own code. Once you learn this vocabulary, you can easily use them in your own routines (and when Ranges come to C++, hopefully it'll also easier to compose them).
There is some litle optimization that may give you a few ms:
int size = numbers.size()-1;
static int counter=1;
static int *num1 = &numbers[0];
static int *num2 = &numbers[1];
for(int n=0;n<size;n++)
{
if(*num1==*num2) counter++;
else
{
cout << counter << "\n";
counter=1;
}
num1++;
num2++;
}
cout<<counter<<endl; //Caution, this line is missing in your code!!
Basicaly:
Avoid access to the vector by id: numbers[n] mean that the computer must multiply n*sizeof(int) and add this result to numbers. Is faster to use pointer and increment it, that mean just an add.