Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement weighted random number generation using maps #44

Merged
merged 12 commits into from Jul 5, 2023

Conversation

DolphyWind
Copy link
Contributor

Closes #43

struct is_map : public std::false_type {};

template<typename Type>
struct is_map<Type, void_t<typename Type::key_type, typename Type::mapped_type, typename Type::value_type>> : public std::true_type{};
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

MappedType random_weight = get(0, total_weight - 1);
MappedType sum = 0;

for(IteratorType it = std::begin(map_container); it != std::end(map_container); ++it)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By iterating through map we're missing reason of using the map instead of vector<pair<Key, Value>>

And for unordered map the order of weights will be different and result of this function will be different

I'm not expert in this algorithm, can it be done with map::upper_bound or map::lower_bound function ?
https://en.cppreference.com/w/cpp/container/map/upper_bound
for logarithmic complexity ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The upper_bound and lower_bound functions operate on the keys of the map so, it is not suitable for this case. And even if it operated on values, we had to store total weight until that object instead of the weight of that object as the value.

Also, the "ordering" of std::unordered_map should not affect the result. Because the probability of each item getting picked is directly tied to its weight.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fine, can you code a simple test case for this inside test/random_test.cpp file ?

I can do it by myself but later next week

Copy link
Contributor Author

@DolphyWind DolphyWind Jul 1, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

During testing, I found out the algorithm fails for floats and doubles as values. I am also fixing that. Also, do you have specific test case(s) that you want me to code.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have nothing special, you can try your best

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All done. But I have one question, should I check whether if there is a negative weight using assert?

It's much better to not allow users to use negative values at all by disallowing it with extra condition for enable_if

Something like this:
!std::is_signed<MapContainer::value_type >::value
And compilation should fail with signed map value types, so users required to use only unsigned types

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For real numbers (float, double, long double) assert is ok

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But this completely disallows users to use reals as weights.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

std::is_unsigned<MapContainer::value_type>::value || is_uniform_real<MapContainer::value_type>::value
...
assert(std::is_unsigned<MapContainer::value_type>::value || map_has_all_positive_real_numbers())

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

details::is_map<MapContainer>::value &&
details::is_iterator<decltype(std::begin(map_container))>::value &&
(details::is_uniform_int<typename MapContainer::mapped_type>::value || details::is_byte<typename MapContainer::mapped_type>::value) &&
std::is_same<Key, common>::value,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

common type is reserved for common type randoms
I think it would be better to add another key type 'weight' for this feature

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

@effolkronium
Copy link
Owner

effolkronium commented Jul 1, 2023

CI windows job failed. Do you have Windows env ?

1 error in CI:
C:\projects\random\test\..\include\effolkronium/random.hpp(1891): error C2039: 'accumulate': is not a member of 'std'
Probably we need add #include <numeric>

2 error: std::numeric_limits<MappedType>::min() need to be wrapped into brackets like this:

(std::numeric_limits<T>::min)( ).

It because the min or max macro from Windows.h conflicts with the numeric_limit static method

@DolphyWind
Copy link
Contributor Author

Now it should work. For some reason, I couldn't get std::accumulate working, so I replaced it with a for loop.

@effolkronium
Copy link
Owner

@DolphyWind Hey, can you fix conflicts with latest merge ?

Now you required to paste the new 'get' methods to only 2 classes basic_random_base and basic_random_local

@DolphyWind
Copy link
Contributor Author

It should be ok now. Test have been passed.

@effolkronium effolkronium merged commit b4bd6c5 into effolkronium:master Jul 5, 2023
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Suggestion: Weighted random number generation using maps.
2 participants