I want to order an std::map by the absolute value of key with following code as the usual customized order
vector<int> A = {-4,4,-2,2};
auto cmp = [](int a, int b) { return abs(a) < abs(b); };
map<int, int, decltype(cmp)> m(cmp);
for (int x : A) m[x]++;
But the result is m = {{-4,2},{-2,2}}. I don't know, why the key 4 and 2 are missing. I want to keep all values and actually I don't care about the = i.e. 4,-4 and -4,4 are both ok for me. I only what to order them when the values are different.
Your comparison function causes your map to treat keys 4 and -4 as equivalent. Ditto for keys 2 and -2, so that explains the results you get, since keys in a map must be unique.
As other answers mention, your comparison operator renders the keys like 4 and -4 equal, as far as the map is concerned.
Solution
You can create your own ordering (with a custom comparison operator) to achieve what you want:
auto cmp = [](int a, int b) {
auto aa = abs(a);
auto bb = abs(b);
return (2 * aa + (aa == a)) < (2 * bb + (bb == b));
};
outputs:
{-2, 1} {2, 1} {-4, 1} {4, 1}
Demo
Explanation
The numbers -4, -2, 2, 4 and so on are integers. Inside the comparison function, you can map them to to natural numbers and create a tie breaker for positive ones by:
taking the absolute of double the negative numbers
taking the absolute of double the positive + 1
So your keys K = {k1, k2, ... kn} behave as
K' = {k1', k2', ..., kn'} where
/ 2 * ki, ki < 0
ki' =
\ 2 * ki + 1, ki > 0
To order the int keys as 0, 1, -1, 2, -2, 3, -3, ... change the comparator to this (live):
auto cmp = [](int a, int b) { return abs(a) != abs(b) ? abs(a) < abs(b) : a > b; };
This comparator uses lexicographical comparison with two sort keys:
The absolute value of the number.
The sign of the number.
If you check the std::map documentation from cppreference.com:
Everywhere the standard library uses the Compare requirements, uniqueness is determined by using the equivalence relation. In imprecise terms, two objects a and b are considered equivalent (not unique) if neither compares less than the other: !comp(a, b) && !comp(b, a).
Because of that, after you've already inserted -4, it will work out that neither of 4 and -4 are less than the other, so deem them equal and increment that map value. If you'd inserted 4 first, you would have had a single entry for 4 instead of -4.
Personally, I consider this confusing and would recommend using a std::map<int, int> and for (auto a : A) m[abs(a)]++;.
Related
I am working on a vector sorting algorithm for my personal particle physics studies but I am very new to coding.
Going through individual scenarios (specific vector sizes and combinations) by brute force becomes extremely chaotic for greater numbers of net vector elements, especially since this whole code will be looped up to 1e5 times.
Take four vectors of 'flavors' A and B: A+, A-, B+, and B-. I need to find two total pairs of elements such that some value k(V+, V-) is maximized with the restriction that different flavors cannot be combined! (V is just a flavor placeholder)
For example:
A+ = {a1+}
A- = {a1-}
B+ = {b1+, b2+}
B- = {b1-}
Since A+ and A- only have one element each, the value k(A+, A-) -> k(a1+, a1-). But for flavor B, there are two possible combinations.
k(b1+, b1-) OR k(b2+, b1-)
I would like to ensure that the combination of elements with the greater value of k is retained. As I said previously, this specific example is not TOO bad by brute force, but say B+ and B- had two elements each? The possible values would be:
k(b1+, b1-) or k(b2+,b2-) or k(b1+, b2-) or k(b2+, b1-)
where only one of these is correct. Furthermore, say two of those four B+B- combinations had greater k than that of A+A-. This would also be valid!
Any help would be appreciated!!! I can clarify if anything above is overly confusing!
I tried something like this,
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
static bool sortbypair(const pair<double, double> &a, const pair<double, double> &b)
{
return (k(a.first, a.second) > k(b.first, b.second)) && k(a.first, b.second) < k(a.second, b.first);
}
But I can't flesh it out.
If I understand your question correctly,
you have a function k which maps two doubles (or a std::pair<double, double>) to a single double. I am assuming double, it wasn't clear from the question. It also isn't strictly at the core of your problem.
you have four std::vector<double>s: aplus, aminus, bplus and bminus.
Your domain are all std::pair<double, double>s that you can form by combining the elements in aplus and aminus as well as all combinations from bplus and bminus respectively.
you want to either
find the pair in your domain that maximizes k
get a collection of all pairs in your domain, sorted by the value of k
Did I get this right? You state in your question
I need to find two total pairs of elements such that some value k(V+, V-) is maximized [...]
which confuses me a bit.
My suggestion is to break down your problem into three subtasks:
Create a range of all combinations of elements in the vectors Vplus and Vminus. This is often denoted as a cartesian product Vplus x Vminus.
Concatenate the ranges created in step 1 for aplus x aminus and bplus x bminus to get a range of all viable pairs in your domain.
Maximize/sort the range from step 2.
Implementation using range-v3
The range-v3 library provides some very convenient tools for this kind of task. Let's assume your k function looks like this:
double k(double x, double y) { return x*x + y*y; }
and your vectors look like this:
std::vector<double> ap{0., 4., 2., 3., 1.};
std::vector<double> am{2., -1.};
std::vector<double> bp{1., 0.5};
std::vector<double> bm{-1., 2.};
Let's define a range representing our domain:
using namespace ranges;
auto pairs_view = view::concat(
view::cartesian_product(ap, am),
view::cartesian_product(bp, bm)
);
The pairs_view instance doesn't actually create the pairs anywhere in memory. It is just an adaptor object that let's you iterate over all pairs that you can construct in the specified way. The pairs are created "lazily" on the fly as you - or an algorithm - iterates over it.
Let's print all pairs from our domain:
auto print = [](auto const& p){
auto first = std::get<0>(p);
auto second = std::get<1>(p);
std::cout << "[" << first << ", " << second << "] k = " << k(first, second) << std::endl;
};
for_each(pairs_view, print);
Output:
[0, 2] k = 4
[0, -1] k = 1
[4, 2] k = 20
[4, -1] k = 17
[2, 2] k = 8
[2, -1] k = 5
[3, 2] k = 13
[3, -1] k = 10
[1, 2] k = 5
[1, -1] k = 2
[1, -1] k = 2
[1, 2] k = 5
[0.5, -1] k = 1.25
[0.5, 2] k = 4.25
Finding the maximum element
Let's start by defining a convenience function (here, in the form of a lambda expression) that evaluates k for a tuple of doubles:
auto k_proj = [](auto const& p){
return k(std::get<0>(p), std::get<1>(p));
};
You can find an iterator to the pair in your domain that maximizes k with just the single line:
auto it = max_element(pairs_view, less{}, k_proj);
print(*it);
Output:
[4, 2] k = 20
The function max_element gets two additional arguments. The first is a comparison function that returns true, if two elements are in order. We provide the default less functor. The second argument is an optional projection that is to be applied on each element before the comparison. We pass k_proj.
Read the above line of code as "Find the element in pairs_view of which the projection onto its k value is maximal, where we want to compare the projected values with the standard less function."
Getting a sorted range of your domain
If you want to have all sorted range of all pairs in your domain, we must create an std::vector<std::pair<double, double>> for your domain first and then sort it. You cannot sort views created with the range-v3 library, because they are just a view into existing objects, they cannot be mutated. In addition, we have to map the special pair types created by the range-v3 library in the cartesian_product functions to actual std::pair<double, double to copy the values into our new container:
auto to_std_pair = [](auto const& p){
return std::pair<double, double>{std::get<0>(p), std::get<1>(p)};
};
auto pairs_vec = pairs_view | view::transform(to_std_pair) | to_vector;
Note that the "pipe" operator | is short-hand notation for the function composition to_vector(view::transform(pairs_view, to_std_pair)).
The invokation of the sorting algorithm looks very similar to the invokation of the max_element algorithm:
sort(pairs_vec, less{}, k_proj);
Let's print the result:
for_each(pairs_vec, print);
Output:
[0, -1] k = 1
[0.5, -1] k = 1.25
[1, -1] k = 2
[1, -1] k = 2
[0, 2] k = 4
[0.5, 2] k = 4.25
[2, -1] k = 5
[1, 2] k = 5
[1, 2] k = 5
[2, 2] k = 8
[3, -1] k = 10
[3, 2] k = 13
[4, -1] k = 17
[4, 2] k = 20
Here is a complete live code example: https://godbolt.org/z/6zo8oj3ah
If you don't want to use the range-v3 library you have two options:
You can wait. Large parts of the range-v3 library have been added to the standard library in C++20. The relevant functions concat, cartesian_product and to_vector will presumably be added in the upcoming standard C++23.
The standard library has max_element and sort. So you could just implement the concatenation and cartesian product on your own: https://godbolt.org/z/7Y5dG16WK
Thank you to everyone who commented!!! I really appreciate your effort. The solution ended up being much simpler than I was making it out to be.
Essentially, from the physics program I'm using, the particles are given in a listed form (ie. 533 e-, 534
p+, 535 e+, etc.). I couldn't figure out how to get range-v3 working (or any external libraries for that matter but thank you for the suggestion) so I figured out to make a tuple out of the indices of combined particles and their associated k value.
#include <iostream>
#include <vector>
#include <algorithm>
#include <tuple>
using namespace std;
static bool weirdsort(const tuple<int, int, double> &a, const tuple<int, int, double> &b)
{
return get<2>(a) > get<2>(b);
}
int main()
{
vector<tuple<int, int, double>> net;
// Sample ptcl list
//
// A+ A- B+ B-
// 0 a1+
// 1 a1-
// 2 b1-
// 3 b1+
// 4 a2+
// 5 a2-
for(int i = 0; i < A+.size(); i++)
{
for (int j = 0; j < A-.size(); j++)
{
net.push_back(A+[i], A-[j], k(A+[i], A-[j]));
}
}
sort(net.begin(), net.end(), weirdsort);
//Now another for loop that erases a tuple (with a lower k value) if it has a repeated ptcl index.
for (int i = 0; i < net.size(); i++)
{
if (get<0>(net[i]) == get<0>(net[i + 1]) || get<1>(net[i]) == get<1>(net[i + 1]))
{
net.erase(net.begin() + i + 1);
}
}
//Now can plot third tuple element of net[0] and net[1]
return 0;
}
It's not quite perfect but since I'm only looking for the first two highest k values it works out just fine. Thanks again!
vector< pair<int,int> > v;
// using indices for comparing pairs
auto func = [&](int i, int j) { return v[i] > v[j]; };
// set which will store the indices and compare keys using
// func based on values in v
set<int, decltype(func)> index_set(func);
If I have two same values v[0]={1,2} and v[1]={1,2} and I insert them into the index_set, i.e.
index_set.insert(0);
index_set.insert(1);
Reality:
I will have only 1 element in index_set (index_set.size()=1), which is only index 0 and index 1 is not inserted.
Expectations: Both 0 and 1 must be inserted.
Rationale:
According to cplusplus, it says:
Unique keys
No two elements in the container can have equivalent keys.
Because keys are not equivalent (0 != 1) so set should contain both 0 and 1. Why is this behaviour justified? I think I am confusing for values of keys for literal values of keys.
Minimal, Complete, Verfiable example: Try this code on ideone!
cplusplus.com states further down on the page:
Compare
...
The set object uses this expression to determine both the order the elements follow in the container and whether two element keys are equivalent (by comparing them reflexively: they are equivalent if !comp(a,b) && !comp(b,a)). No two elements in a set container can be equivalent.
You've specified a custom comparison function that states that elements 0 and 1 should be considered equal. Therefore your std::set will keep only one of them.
The compare function you are using is not very intuitive. The way it's been implemented, 0 and 1 are considered equivalent. If you insert 1 first, 0 will not be inserted.
If you have
v[0] = {1, 2};
v[1] = {1, 2};
v[2] = {2, 2};
v[3] = {1, 2}
Then, 0, 1, and 3 will be considered equivalent as far as the set is concerned.
There's array of size n. The values can be between 0 and (n-1) as the indices.
For example: array[4] = {0, 2, 1, 3}
I should say if there's any number that is repeated more than 1 time.
For example: array[5] = {3,4,1,2,4} -> return true because 4 is repeated.
This question has so many different solutions and I would like to know if this specific solution is alright (if yes, please prove, else refute).
My solution (let's look at the next example):
array: indices 0 1 2 3 4
values 3 4 1 2 0
So I suggest:
count the sum of the indices (4x5 / 2 = 10) and check that the values' sum (3+4+1+2+0) is equal to this sum. if not, there's repeated number.
in addition to the first condition, get the multiplication of the indices(except 0. so: 1x2x3x4) and check if it's equal to the values' multiplication (except 0, so: 3x4x1x2x0).
=> if in each condition, it's equal then I say that there is NO repeated number. otherwise, there IS a repeated number.
Is it correct? if yes, please prove it or show me a link. else, please refute it.
Why your algorithm is wrong?
Your solution is wrong, here is a counter example (there may be simpler ones, but I found this one quite quickly):
int arr[13] = {1, 1, 2, 3, 4, 10, 6, 7, 8, 9, 10, 11, 6};
The sum is 78, and the product is 479001600, if you take the normal array of size 13:
int arr[13] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
It also has a sum of 78 and a product of 479001600 so your algorithm does not work.
How to find counter examples?1
To find a counter example2 3:
Take an array from 0 to N - 1;
Pick two even numbers3 M1 > 2 and M2 > 2 between 0 and N - 1 and halve them;
Replace P1 = M1/2 - 1 by 2 * P1 and P2 = M2/2 + 1 by 2 * P2.
In the original array you have:
Product = M1 * P1 * M2 * P2
Sum = 0 + M1 + P1 + M2 + P2
= M1 + M1/2 - 1 + M2 + M2/2 + 1
= 3/2 * (M1 + M2)
In the new array you have:
Product = M1/2 * 2 * P1 + M2/2 * 2 * P2
= M1 * P1 * M2 * P2
Sum = M1/2 + 2P1 + M2/2 + 2P2
= M1/2 + 2(M1/2 - 1) + M2/2 + 2(M2/2 + 1)
= 3/2 * M1 - 2 + 3/2 * M2 + 2
= 3/2 * (M1 + M2)
So both array have the same sum and product, but one has repeated values, so your algorithm does not work.
1 This is one method of finding counter examples, there may be others (there are probably others).
2 This is not exactly the same method I used to find the first counter example - In the original method, I used only one number M and was using the fact that you can replace 0 by 1 without changing the product, but I propose a more general method here in order to avoid argument such as "But I can add a check for 0 in my algorithm.".
3 That method does not work with small array because you need to find 2 even numbers M1 > 2 and M2 > 2 such that M1/2 != M2 (and reciprocally) and M1/2 - 1 != M2/2 + 1, which (I think) is not possible for any array with a size lower than 14.
What algorithms do work?4
Algorithm 1: O(n) time and space complexity.
If you can allocate a new array of size N, then:
template <std::size_t N>
bool has_repetition (std::array<int, N> const& array) {
std::array<bool, N> rep = {0};
for (auto v: array) {
if (rep[v]) {
return true;
}
rep[v] = true;
}
return false;
}
Algorithm 2: O(nlog(n)) time complexity and O(1) space complexity, with a mutable array.
You can simply sort the array:
template <std::size_t N>
bool has_repetition (std::array<int, N> &array) {
std::sort(std::begin(array), std::end(array));
auto it = std::begin(array);
auto ne = std::next(it);
while (ne != std::end(array)) {
if (*ne == *it) {
return true;
}
++it; ++ne;
}
return false;
}
Algorithm 3: O(n^2) time complexity and O(1) space complexity, with non mutable array.
template <std::size_t N>
bool has_repetition (std::array<int, N> const& array) {
for (auto it = std::begin(array); it != std::end(array); ++it) {
for (auto jt = std::next(it); jt != std::end(array); ++jt) {
if (*it == *jt) {
return true;
}
}
}
return false;
}
4 These algorithms do work, but there may exist other ones that performs better - These are only the simplest ones I could think of given some "restrictions".
What's wrong with your method?
Your method computes some statistics of the data and compares them with those expected for a permutation (= correct answers). While a violation of any of these comparisons is conclusive (the data cannot satisfy the constraint), the inverse is not necessarily the case. You only look at two statistics, and these are too few for sufficiently large data sets. Owing to the fact that the data are integer, the smallest number of data for which your method may fail is larger than 3.
If you are searching duplicates in your array there is simple way:
int N =5;
int array[N] = {1,2,3,4,4};
for (int i = 0; i< N; i++){
for (int j =i+1; j<N; j++){
if(array[j]==array[i]){
std::cout<<"DUPLICATE FOUND\n";
return true;
}
}
}
return false;
Other simple way to find duplicates is using the std::set container for example:
std::set<int> set_int;
set_int.insert(5);
set_int.insert(5);
set_int.insert(4);
set_int.insert(4);
set_int.insert(5);
std::cout<<"\nsize "<<set_int.size();
the output will be 2, because there is 2 individual values
A more in depth explanation why your algorithm is wrong:
count the sum of the indices (4x5 / 2 = 10) and check that the values' sum (3+4+1+2+0) is equal to this sum. if not, there's repeated number.
Given any array A which has no duplicates, it is easy to create an array that meets your first requirement but now contains duplicates. Just take take two values and subtract one of them by some value v and add that value to the other one. Or take multiple values and make sure the sum of them stays the same. (As long as new values are still within the 0 .. N-1 range.) For N = 3 it is already possible to change {0,1,2} to {1,1,1}. For an array of size 3, there are 7 compositions that have correct sum, but 1 is a false positive. For an array of size 4 there are 20 out of 44 have duplicates, for an array of size 5 that's 261 out of 381, for an array of size 6 that's 3612 out of 4332, and so on. It is save to say that the number of false positives grows much faster than real positives.
in addition to the first condition, get the multiplication of the indices(except 0. so: 1x2x3x4) and check if it's equal to the values' multiplication (except 0, so: 3x4x1x2x0).
The second requirement involves the multiplication of all indices above 0. It is easy to realize this is could never be a very strong restriction either. As soon as one of the indices is not prime, the product of all indices is no longer uniquely tied to the multiplicands and a list can be constructed of different values with the same result. E.g. a pair of 2 and 6 can be replaced with 3 and 4, 2 and 9 can be replaced with 6 and 3 and so on. Obviously the number of false positives increases as the array-size gets larger and more non-prime values are used as multiplicands.
None of these requirements is really strong and the cannot compensate for the other. Since 0 is not even considered for the second restriction a false positive can be created fairly easy for arrays starting at size 5. any pair of 0 and 4 can simply be replaced with two 2's in any unique array, for example {2, 1, 2, 3, 2}
What you would need, is to have a result that is uniquely tight to the occurring values. You could tweak your second requirement to a more complex approach and skip over the non-prime values and take 0 into account. For example you could use the first prime as multiplicand (2) for 0, use 3 as multiplicand for 1, 5 as multiplicand for 2, and so on. That would work (you would not need the first requirement), but this approach would be overly complex. An simpler way to get a unique result would be to OR the i-th bit for each value (0 => 1 << 0, 1 => 1 << 1, 2 => 1 << 2, and so on. (Obviously it is faster to check wether a bit was already set by a reoccurring value, rather than wait for the final result. And this is conceptually the same as using a bool array/vector from the other examples!)
What would be the most optimal method in c++ to discover all pairs of positive integer numbers that fit a formula. For example:
a^2 * b = 16;//a & b MUST be positive INT.
How could I find all the combinations of a and b that will fit the formula?
Edit: For more clarity, this is just an example. Really I have a^2 * b = c where c is incrementing using a for loop and I need to find every positive integer pair (a,b) that will fit the criteria of this equation.
The problem is to find all pairs of positive integers (a,b) that satisfy the equation a^2 * b = c where c is also a positive integer.
From the equation, c is divisible by a perfect square. So first, we find all perfect squares that divide c evenly. Trivially, a=1, b=c satisfies this, so we know that every value of c has at least one solution. After finding every a, we divide c by each of the a^2 to yield its corresponding b.
Here's the above implemented in C++:
std::vector<std::pair<int, int> > solve(int c) {
std::vector<int> a;
for (int i = 1; i * i <= c; ++i)
if (c % (i*i) == 0) a.push_back(i);
std::vector<std::pair<int, int> > solutions;
solutions.reserve(a.size());
for (std::vector<int>::iterator it = a.begin(); it != a.end(); ++it) {
const int& a = *it;
solutions.push_back(std::pair<int, int>(a, c / (a*a)));
}
return solutions;
}
Here's a live example, showing solutions for c = 7! = 5040.
Find the factors of c and then find the subset that use three values, two of which are the same.
By inspection, the three integer solutions are:
a = ±1, b = 16
a = ±2, b = 4
a = ±4, b = 1
Otherwise, there are infinite solutions. Wolfram|Alpha gives the details.
If you need to do this repeatedly, this is what I think the most efficient way to solve this problem:
1. Setup a square table, [0, 1, 4, 9, 16, ......], the length of the table depends on the maximum number you are going to deal with, anyway, you will need O(sqrt(N)) space for the lookup table.
2. divide the number into factors, for example, 16 has 5 factors (1, 2, 4, 8, 16).
3. from checking lookup table, you can find 1, 4, and 16 are in the lookup table. so there are 6 pairs, namingly, (-1 or 1)^2 * 16, (-2 or 2)^2 * 4, and (-4 or 4)^2 * 1.
An interview question:
Given two non-ordered integer sequences a and b, their size is n, all
numbers are randomly chosen: Exchange the elements of a and b, such that the sum of the elements of a minus the sum of the elements of b is minimal.
Given the example:
a = [ 5 1 3 ]
b = [ 2 4 9 ]
The result is (1 + 2 + 3) - (4 + 5 + 9) = -12.
My algorithm: Sort them together and then put the first smallest n ints in a and left in b. It is O(n lg n) in time and O(n) in space. I do not know how to improve it to an algorithm with O(n) in time and O(1) in space. O(1) means that we do not need more extra space except seq 1 and 2 themselves.
Any ideas ?
An alternative question would be: What if we need to minimize the absolute value of the differences (minimize |sum(a) - sum(b)|)?
A python or C++ thinking is preferred.
Revised solution:
Merge both lists x = merge(a,b).
Calculate median of x (complexity O(n) See http://en.wikipedia.org/wiki/Selection_algorithm )
Using this median swap elements between a and b. That is, find an element in a that is less than median, find one in b that is more than median and swap them
Final complexity: O(n)
Minimizing absolute difference is NP complete since it is equivalent to the knapsack problem.
What comes into my mind is following algorithm outline:
C = A v B
Partitially sort #A (number of A) Elements of C
Subtract the sum of the last #B Elements from C from the sum of the first #A Elements from C.
You should notice, that you don't need to sort all elements, it is enough to find the number of A smallest elements. Your example given:
C = {5, 1, 3, 2, 4, 9}
C = {1, 2, 3, 5, 4, 9}
(1 + 2 + 3) - (5 + 4 + 9) = -12
A C++ solution:
#include <iostream>
#include <vector>
#include <algorithm>
int main()
{
// Initialize 'a' and 'b'
int ai[] = { 5, 1, 3 };
int bi[] = { 2, 4, 9 };
std::vector<int> a(ai, ai + 3);
std::vector<int> b(bi, bi + 3);
// 'c' = 'a' merged with 'b'
std::vector<int> c;
c.insert(c.end(), a.begin(), a.end());
c.insert(c.end(), b.begin(), b.end());
// partitially sort #a elements of 'c'
std::partial_sort(c.begin(), c.begin() + a.size(), c.end());
// build the difference
int result = 0;
for (auto cit = c.begin(); cit != c.end(); ++cit)
result += (cit < c.begin() + a.size()) ? (*cit) : -(*cit);
// print result (and it's -12)
std::cout << result << std::endl;
}