MultiIndex containers -- offering vector and set access - c++

I have an application where, first, an std::vector<int> object is generated. Then, some operations need to be performed on this object viewed as an std::set<int> where the order does not matter and repetitions don't count.
At present, I explicitly construct an object of type std::set<int> from the std::vector<int> object. An example is presented below:
#include <cstdio>
#include <set>
#include <vector>
void printset(std::set<int>& Set) {
printf("Printing Set Elements: ");
for (std::set<int>::iterator siter = Set.begin(); siter != Set.end(); ++siter) {
int val = *siter;
printf("%d ", val);
}
printf("\n");
}
void printvec(std::vector<int>& Vec) {
printf("Printing Vec Elements: ");
for (size_t i = 0, szi = Vec.size(); i < szi; ++i) {
int val = Vec[i];
printf("%d ", val);
}
printf("\n");
}
int main()
{
std::vector<int> VecInt{ 6, 6, 5, 5, 4, 4 };
std::set<int> SetInt(VecInt.begin(), VecInt.end());
printvec(VecInt);
printset(SetInt);
}
I am trying to see if I can use Boost.MultiIndex for this purpose. One introduction to Boost.MultiIndex states:
Boost.MultiIndex can be used if elements need to be accessed in different ways and would normally need to be stored in multiple containers. Instead of having to store elements in both a vector and a set and then synchronizing the containers continuously, you can define a container with Boost.MultiIndex that provides a vector interface and a set interface.
and this is precisely what I am doing (using multiple containers and then creating one from the other constantly) while I would like to create a (multi-index) container once and then provide a vector interface and a set interface.
On looking through various examples, for e.g., here and here, it is not clear how those examples can be modified to the code example above.
Ideally, I would like to do the following in the code example above:
MultiIndexContainer vec_set_container;
vec_set_container.push_back(6);//or anything equivalent for the MultiIndexContainer
vec_set_container.push_back(6);
vec_set_container.push_back(5);
vec_set_container.push_back(5);
vec_set_container.push_back(4);
vec_set_container.push_back(4);
printvec(vec_set_container.Some_Function_That_Exposes_Vector_Interface());
printset(vec_set_container.Some_Function_That_Exposes_Set_Interface());
How can this be accomplished using Boost.MultiIndex ?

Random access index would match the "vector" interface.
An ordered unique index would match the "set" interface.
However, if you have a unique index, this will prevent insertion of duplicates. So, you would get:
Live On Compiler Explorer
#include <boost/multi_index/identity.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/random_access_index.hpp>
#include <boost/multi_index_container.hpp>
#include <fmt/ranges.h>
namespace bmi = boost::multi_index;
using Table = bmi::multi_index_container< //
int,
bmi::indexed_by<
bmi::random_access<bmi::tag<struct asVector>>,
bmi::ordered_unique<bmi::tag<struct asSet>, bmi::identity<int>>>>;
int main()
{
Table data{ 6, 6, 5, 5, 4, 4 };
fmt::print("As vec {}\nAs set {}\n", //
data.get<asVector>(), //
data.get<asSet>());
}
Printing
As vec {6, 5, 4}
As set {4, 5, 6}
Now, I think the "best" you could do with this is to make the order index non-unique (so, mimicking a std::multiset instead of std::set): Live On Compiler Explorer
bmi::ordered_non_unique<bmi::tag<struct asSet>, bmi::identity<int>>
Printing
As vec [6, 6, 5, 5, 4, 4]
As set {4, 4, 5, 5, 6, 6}
If you want to traverse unique elements, using a range adaptor would be minimally costly:
Using Boost Live
fmt::print("As vec {}\nAs set {}\n", //
data.get<asVector>(), //
data.get<asSet>() | boost::adaptors::uniqued);
Using RangeV3 Live
fmt::print("As vec {}\nAs set {}\n", //
data.get<asVector>(), //
data.get<asSet>() | ranges::views::unique);
Using Standard Ranges; I couldn't make this work but it should really be something like std::ranges::unique(data.get<asSet>())
All printing
As vec {6, 6, 5, 5, 4, 4}
As set {4, 5, 6}
Other Ideas
If you don't require random access, then sequenced index might be preferrable for you. And note that this interface comes with the handy unique() and sort() methods (just like std::list).
UPDATE To The Comments
Here's a rolled-in-one response to the comments:
Live On Compiler Explorer
#include <boost/multi_index/identity.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/sequenced_index.hpp>
#include <boost/multi_index_container.hpp>
#include <fmt/ranges.h>
#include <random>
namespace bmi = boost::multi_index;
template <typename T>
using ListSet = bmi::multi_index_container< //
T,
bmi::indexed_by<
bmi::sequenced<bmi::tag<struct byInsertion>>, //
bmi::ordered_unique<bmi::tag<struct Ordered>, bmi::identity<T>> //
>>;
int main()
{
ListSet<int> data;
std::mt19937 prng{99}; // "random" seed
for (std::uniform_int_distribution d(1, 10); data.size() < 5;) {
int el = d(prng);
if (auto [it, unique] = data.push_back(el); not unique) {
fmt::print("Element {} duplicates index #{}\n", el,
std::distance(begin(data), it));
}
}
fmt::print("By insertion {}\nOrdered {}\n", data, data.get<Ordered>());
}
Prints
Element 9 duplicates index #3
Element 8 duplicates index #1
By insertion [7, 8, 5, 9, 1]
Ordered {1, 5, 7, 8, 9}

Related

How can I use C++ copy for reverse elements?

I want to reverse the elements in vector, so I use reverse function and copy function. reverse function act well but copy function got wrong result.
#include <iostream>
#include <vector>
#include <list>
#include <algorithm>
#include "show.h"
int main()
{
std::vector<int> v = { 1,2,3,4,5,6,7,8,9,10 };
std::list<int> s = { 1,2,3,4,5,6,7,8,9,10 };
std::copy(v.rbegin(), v.rend(), v.begin());
show(v); // expected result: 10, 9, 8, 7, 6, 5, 4, 3, 2, 1
// result: 10, 9, 8, 7, 6, 6, 7, 8, 9, 10
return 0;
}
what's the problem with my code?
what's the problem with my code?
Because it's reading and writing from the same vector. It's reading from one side, and writing from the other side.
When the two sides meet in the middle, the reading side continues to read what was already written from the other side. Hilarity ensues.
Your expected results would be as if the original, pristine contents of the vector get read from start to finish. But you seem to be forgetting that, as the vector gets read, the other end of the vector starts to get immediately overwritten by the writing side of the copy.
That's the problem with your code.

I don't understand how to use set_difference correctly

In the task it was said that it is necessary to get the "value" of the largest elements from the array. Then it should be compared with the second array, and duplicates should be excluded.
In the task it is necessary to use partial_sort_copy in vector, and set_difference.
The problem is that when you start the program crashes at all, even without showing what exactly the error is, that's why I am writing here
I looked at several sites with examples of using this function, but I used everything as there, and do not quite understand why it does not work and crashes.
#include<iostream>
#include<vector>
#include <algorithm>
using namespace std;
int main()
{
int value = 5;
int A_ints[] { 1, 4, 12, 5, 1, 4, 6, 9, 0, 3 };
vector<int> A_vec(value);
vector<int> B_vec { 13, 12, 11, 10 };
vector<int> C_vec;
vector<int> D_vec {9, 6, 5, 4};
partial_sort_copy(A_ints, A_ints + 9, A_vec.begin(), A_vec.end(), greater<int>());
set_difference(A_vec.begin(), A_vec.end(), B_vec.begin(), B_vec.end(), C_vec.begin(), greater<int>());
if (ะก_vec == D_vec)
cout << "Success \n";
else
cout << "Failure \n";
system("pause");
return 0;
}
As a result, if set_difference will works correctly, then the last condition should return "Success".
The 5th argument to set_difference is the parameter that the results of the algorithm will be written to.
You've passed in C_vec.begin() which is an iterator pointing to an empty vector. It's undefined behavior to write to an empty vector's iterator.
You have several issues here, but one solution for this particular problem would be to replace C_vec.begin() with an insert_iterator:
inserter(C_vec, begin(C_vec))

Implementing partition_unique and stable_partition_unique algorithms

I'm looking for a way to partition a set of ordered elements such that all unique elements occur before their respective duplicates, noting that std::unique is not applicable as duplicate elements are overwritten, I thought of using std::partition. Calling this algorithm partition_unique, I also need the corresponding stable_partition_unique (i.e. like stable_partition).
A basic implementation of partition_unique is:
#include <algorithm>
#include <iterator>
#include <unordered_set>
#include <functional>
template <typename BidirIt, typename BinaryPredicate = std::equal_to<void>>
BidirIt partition_unique(BidirIt first, BidirIt last, BinaryPredicate p = BinaryPredicate {})
{
using ValueTp = typename std::iterator_traits<BidirIt>::value_type;
std::unordered_set<ValueTp, std::hash<ValueTp>, BinaryPredicate> seen {};
seen.reserve(std::distance(first, last));
return std::partition(first, last,
[&p, &seen] (const ValueTp& value) {
return seen.insert(value).second;
});
}
Which can be used like:
#include <vector>
#include <iostream>
int main()
{
std::vector<int> vals {1, 1, 2, 4, 5, 5, 5, 7, 7, 9, 10};
const auto it = partition_unique(std::begin(vals), std::end(vals));
std::cout << "Unique values: ";
std::copy(std::begin(vals), it, std::ostream_iterator<int> {std::cout, " "}); // Unique values: 1 10 2 4 5 9 7
std::cout << '\n' << "Duplicate values: ";
std::copy(it, std::end(vals), std::ostream_iterator<int> {std::cout, " "}); // Duplicate values: 7 5 5 1
}
The corresponding stable_partition_unqiue can be achieved by replacing std::partition with std::stable_partition.
The problem with these approaches is that they unnecessarily buffer all unique values in the std::unordered_set (which also adds a hash function requirement), which shouldn't be required as the elements are sorted. It's not too much work to come up with a better implementation for partition_unique, but an implementation of stable_partition_unique seems considerably more difficult, and I'd rather not implement this myself if possible.
Is there a way to use existing algorithms to achieve optimal partition_unique and stable_ partition_unique algorithms?
Create a queue to hold the duplicates. Then, initialize two indexes, src and dest, starting at index 1, and go through the list. If the current item (list[src]) is equal to the previous item (list[dest-1]), then copy it to the queue. Otherwise, copy it to list[dest] and increment dest.
When you've exhausted the list, copy items from the queue to the tail of the original list.
Something like:
Queue dupQueue
int src = 1
int dest = 1
while (src < list.count)
{
if (list[src] == list[dest-1])
{
// it's a duplicate.
dupQueue.push(list[src])
}
else
{
list[dest] = list[src]
++dest
}
++src
}
while (!dupQueue.IsEmpty)
{
list[dest] = dupQueue.pop()
++dest
}
I know the STL has a queue. Whether it has an algorithm similar to the above, I don't know.

Update map in-place

I have a piece of code manipulating a map<int,int> object. Let's say that at some point I want to double all the entries, keys and values. Is a there a way to do it in place, without creating a new map, looping and entering updated entries?
My concern is saving memory space.
Doubling the values is possible like this:
std::map<int,int> my_map;
for(auto& item:my_map){
item.second*=2; // double all values
}
However, it is not possible to double the Key since item is from the type std::pair<const int,int>. Notice the const for the Key.
Suggestion:
I think std::map is not best container for this case. Try this way:
std::vector<std::pair<int,int>> my_simi_map;
for(auto& my_simi_map){
item.first*=2; // double all keys
item.second*=2; // double all values
}
Edit:
My concern is saving memory space.
If it is just memory problem then you can pick an item from the map and insert a doubled version of it in the new map and directly delete it from the old map. In this case you will loose the size of just one element of the map not a whole another map.
You can traverse the map in reverse order of key for positive keys, in order to circumvent side effects, and create the new key/value pairs. You can do the equivalent for negative keys.
#include <cstdio>
#include <algorithm>
#include <map>
using namespace std;
int main() {
map<int, int> m = {
{10, 20},
{-5, 23},
{-10, 7},
{20, 30},
{15, 21},
{18, 2},
};
for (auto it = m.begin(); it != m.end() && it->first < 0; it = m.erase(it)) {
m[it->first * 2] = 2 * it->second;
}
for (auto it = m.rbegin(); it != m.rend() && it->first > 0; it++) {
m[it->first * 2] = 2 * it->second;
m.erase(----it.base());
}
for (auto &p: m) {
printf("%d, %d\n", p.first, p.second);
}
}
Output:
-20, 14
-10, 46
20, 40
30, 42
36, 4
40, 60

Finding vectors in C++

I have two vectors of vector<unsigned> namely: vector<vector<unsigned> > sbp, vector<vector<unsigned> > sp. I want to print all those vectors in sbp which are also in sp. Both vectors sbp and sp are stored (i) first by size; (ii) and when the size are equal, then the vectors are sorted lexicographically. I wrote the following code for doing the same. The code appears to be giving segmentation fault. I debugged the code (by printing out values), but I am not able to find the source of the error.
Can someone please help me find what could be the source of segmentation fault. Also if there is some algorithm which is faster than this, then that will be really great
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
int main()
{
vector<vector<unsigned> > sbp;
vector<vector<unsigned> > sp;
vector<vector<unsigned> >::iterator itSP=sp.begin();
for(vector<vector<unsigned> >::iterator itSbp=sbp.begin(),lSbp=sbp.end();itSbp!=lSbp;)
{
if(std::lexicographical_compare(((*itSbp)).begin(), ((*itSbp)).end(), (*itSP).begin(), (*itSP).end()))
{
itSbp++;
}else{
if((*itSbp)==(*itSP))
{
// cout<<(*itSbp)<<"\n";
itSbp++;
}else{
itSP++;
}
}
}
}
I am using C++11(gcc 4.8)
I want to print all those vectors in sbp which are also in sp.
Whenever there is a situation where you want to collate the common values that are in two containers, the algorithm that should spring to mind right away is std::set_intersection.
The caveat is that std::set_intersection requires that the containers contain sorted values (or a functor provided that describes the sort order). However, if you can meet that criteria, then the solution is trivial.
Here is an example:
#include <vector>
#include <iostream>
#include <algorithm>
#include <iterator>
using namespace std;
int main()
{
// test data
vector<vector<unsigned> > sbp = { { 1, 2, 3, 4, 6, 7 }, { 1, 2, 3, 6, 7 }, { 2, 3, 4, 6, 7 }};
vector<vector<unsigned> > sp = { { 1, 2, 3, 4, 6, 7 }, { 1, 2, 3, 4, 6, 7, 8 }, { 2, 3, 4, 6, 7 }};
// resulting vector
vector<vector<unsigned> > result;
// get the intersection of the two values
set_intersection(sbp.begin(), sbp.end(), sp.begin(), sp.end(), back_inserter(result));
// output the results
for_each(result.begin(), result.end(), [](const std::vector<unsigned>& v)
{copy(v.begin(), v.end(), ostream_iterator<unsigned>(cout, " ")); cout << "\n";});
}
Live Example
If the number of items are large, and there is a good possibility that the data contains duplicates, then a std::set can be used to store the data.
// resulting vector
std::set<vector<unsigned>> result;
// get the intersection of the two values
set_intersection(sbp.begin(), sbp.end(), sp.begin(), sp.end(), std::inserter(result, result.begin()));