I'm beginner in C++. Before I work with C#. Bellow is a C# script. How I can do same these things in native C++?
All I need is:
A list, or similar, have int-int key-value pair
Can auto sort by value. If not, it must be sortable by key and it can get index of a
value (each of my values is definite)
I tried std::map but it don't have built-in sort by value or get key by value. Is C++ have similar thing like sortedlist in c#?
Thank you very much!
public static SortedList<int, int> sortedList1 = new SortedList<int, int>();
static void List_Add(int i) // 0 < i < 1000
{
if (!sortedList1.ContainsValue(i))
sortedList1[Environment.TickCount] = i;
}
static void List_Remove(int i) // 0 < i < 1000
{
if (sortedList1.ContainsValue(i))
sortedList1.RemoveAt(sortedList1.IndexOfValue(i));
}
static int List_toInt()
{
int time = 0;
int keys = 0;
bool modifier = false;
foreach (KeyValuePair<int, int> i in sortedList1)
{
if (i.Value > 90) modifier = true;
if (i.Key - time > 200 | modifier | keys > 1000)
{
keys = keys * 1000 + i.Value;
time = i.Key;
}
}
return keys;
}
You seem to be doing the wrong way round as usually things are sorted using keys and queries are done using the key not using the value. However, it seems a std::map<int,int> will help you here. Simply use your value as a key of the map and your key as value(so that you can query using the value). Use multimap if duplicates are allowed.
This are some converter tools:Please visit the following links:
http://sourceforge.net/projects/convetercpptocs/
http://www.tangiblesoftwaresolutions.com/Product_Details/CSharp_to_CPlusPlus_Converter_Details.html
http://cscpp.codeplex.com/
Like that:
#include <map>
#include "Winbase.h"
std::map<int, int> sortedList1;
void List_Add(int i) // 0 < i < 1000
{
if (sortedList1.find(i) == sortedList1.end())
sortedList1.insert(std::make_pair<int, int>(GetTickCount(), i));
}
void List_Remove(int i) // 0 < i < 1000
{
if (sortedList1.find(i) != sortedList1.end())
sortedList1.erase(sortedList1.find(i));
}
int List_toInt()
{
int time = 0;
int keys = 0;
bool modifier = false;
for (std::map<int, int>::const_iterator it = sortedList1.cbegin();
it != sortedList1.cend(); it++)
{
if (it->second > 90) modifier = true;
if (it->first - time > 200 || modifier || keys > 1000)
{
keys = keys * 1000 + it->second;
time = it->first;
}
}
return keys;
}
Related
This is a2.hpp, and is the program that can be edited, as far as I know the code is correct, just too slow. I am honestly lost here, I know my for loops are probably whats slowing me down so much, maybe use an iterator?
// <algorithm>, <list>, <vector>
// YOU CAN CHANGE/EDIT ANY CODE IN THIS FILE AS LONG AS SEMANTICS IS UNCHANGED
#include <algorithm>
#include <list>
#include <vector>
class key_value_sequences {
private:
std::list<std::vector<int>> seq;
std::vector<std::vector<int>> keyref;
public:
// YOU SHOULD USE C++ CONTAINERS TO AVOID RAW POINTERS
// IF YOU DECIDE TO USE POINTERS, MAKE SURE THAT YOU MANAGE MEMORY PROPERLY
// IMPLEMENT ME: SHOULD RETURN SIZE OF A SEQUENCE FOR GIVEN KEY
// IF NO SEQUENCE EXISTS FOR A GIVEN KEY RETURN 0
int size(int key) const;
// IMPLEMENT ME: SHOULD RETURN POINTER TO A SEQUENCE FOR GIVEN KEY
// IF NO SEQUENCE EXISTS FOR A GIVEN KEY RETURN nullptr
const int* data(int key) const;
// IMPLEMENT ME: INSERT VALUE INTO A SEQUENCE IDENTIFIED BY GIVEN KEY
void insert(int key, int value);
}; // class key_value_sequences
int key_value_sequences::size(int key) const {
//checks if the key is invalid or the count vector is empty.
if(key<0 || keyref[key].empty()) return 0;
// sub tract 1 because the first element is the key to access the count
return keyref[key].size() -1;
}
const int* key_value_sequences::data(int key) const {
//checks if key index or ref vector is invalid
if(key<0 || keyref.size() < static_cast<unsigned int>(key+1)) {
return nullptr;
}
// ->at(1) accesses the count (skipping the key) with a pointer
return &keyref[key].at(1);
}
void key_value_sequences::insert(int key, int value) {
//checks if key is valid and if the count vector needs to be resized
if(key>=0 && keyref.size() < static_cast<unsigned int>(key+1)) {
keyref.resize(key+1);
std::vector<int> val;
seq.push_back(val);
seq.back().push_back(key);
seq.back().push_back(value);
keyref[key] = seq.back();
}
//the index is already valid
else if(key >=0) keyref[key].push_back(value);
}
#endif // A2_HPP
This is a2.cpp, this just tests the functionality of a2.hpp, this code cannot be changed
// DO NOT EDIT THIS FILE !!!
// YOUR CODE MUST BE CONTAINED IN a2.hpp ONLY
#include <iostream>
#include "a2.hpp"
int main(int argc, char* argv[]) {
key_value_sequences A;
{
key_value_sequences T;
// k will be our key
for (int k = 0; k < 10; ++k) { //the actual tests will have way more than 10 sequences.
// v is our value
// here we are creating 10 sequences:
// key = 0, sequence = (0)
// key = 1, sequence = (0 1)
// key = 2, sequence = (0 1 2)
// ...
// key = 9, sequence = (0 1 2 3 4 5 6 7 8 9)
for (int v = 0; v < k + 1; ++v) T.insert(k, v);
}
T = T;
key_value_sequences V = T;
A = V;
}
std::vector<int> ref;
if (A.size(-1) != 0) {
std::cout << "fail" << std::endl;
return -1;
}
for (int k = 0; k < 10; ++k) {
if (A.size(k) != k + 1) {
std::cout << "fail";
return -1;
} else {
ref.clear();
for (int v = 0; v < k + 1; ++v) ref.push_back(v);
if (!std::equal(ref.begin(), ref.end(), A.data(k))) {
std::cout << "fail 3 " << A.data(k) << " " << ref[k];
return -1;
}
}
}
std::cout << "pass" << std::endl;
return 0;
} // main
If anyone could help me improve my codes efficiency I would really appreciate it, thanks.
First, I'm not convinced your code is correct. In insert, if they key is valid you create a new vector and insert it into sequence. Sounds wrong, as that should only happen if you have a new key, but if your tests pass it might be fine.
Performance wise:
Avoid std::list. Linked lists have terrible performance on today's hardware because they break pipelineing, caching and pre-fetching. Always use std::vector instead. If the payload is really big and you are worried about copies use std::vector<std::unique_ptr<T>>
Try to avoid copying vectors. In your code you have keyref[key] = seq.back() which copies the vector, but should be fine since it's only one element.
Otherwise there's no obvious performance problems. Try to benchmark and profile your program and see where the slow parts are. Usually there's one or two places that you need to optimize and get great performance. If it's still too slow, ask another question where you post your results so that we can better understand the problem.
I will join Sorin in saying don't use std::list if avoidable.
So you use key as direct index, where does it say it is none-negative? where does it say its less than 100000000?
void key_value_sequences::insert(int key, int value) {
//checks if key is valid and if the count vector needs to be resized
if(key>=0 && keyref.size() < static_cast<unsigned int>(key+1)) {
keyref.resize(key+1); // could be large
std::vector<int> val; // don't need this temporary.
seq.push_back(val); // seq is useless?
seq.back().push_back(key);
seq.back().push_back(value);
keyref[key] = seq.back(); // we now have 100000000-1 empty indexes
}
//the index is already valid
else if(key >=0) keyref[key].push_back(value);
}
Can it be done faster? depending on your key range yes it can. You will need to implement a flat_map or hash_map.
C++11 concept code for a flat_map version.
// effectively a binary search
auto key_value_sequences::find_it(int key) { // type should be iterator
return std::lower_bound(keyref.begin(), keyref.end(), [key](const auto& check){
return check[0] < key; // key is 0-element
});
}
void key_value_sequences::insert(int key, int value) {
auto found = find_it(key);
// at the end or not found
if (found == keyref.end() || found->front() != key) {
found = keyref.emplace(found, key); // add entry
}
found->emplace_back(value); // update entry, whether new or old.
}
const int* key_value_sequences::data(int key) const {
//checks if key index or ref vector is invalid
auto found = find_it(key);
if (found == keyref.end())
return nullptr;
// ->at(1) accesses the count (skipping the key) with a pointer
return found->at(1);
}
(hope I got that right ...)
I have used std::vector for making my algorithm. I would like to replace the vectors by linked lists.
In order to do so, I was thinking of using the std::list, but I have no idea how to do this, for example I have tried following example for finding a value within a vector/list:
void find_values_in_vector(const std::vector<int>& input_vector, int value, int &rv1, int &rv2)
{
if (input_vector[0] >= value) { // too small
rv1 = 0; rv2 = 0; return;
}
int index = (int)input_vector.size() - 1;
if (input_vector[index] <= value) { // too big
rv1 = index; rv2 = index; return;
}
// somewhere inside
index = 0;
while (input_vector[index] <= value) {
index++;
}
rv1 = index - 1; rv2 = index; return;
}
void find_values_in_list(const std::list<int>& input_list, int value, int &rv1, int &rv2)
{
if (*input_list.begin() >= value) { // too small
rv1 = 0; rv2 = 0; return;
}
if (*input_list.end() <= value) { // too big
rv1 = (int)input_list.size() - 1; rv2 = (int)input_list.size() - 1; return;
}
// somewhere inside
int index = 0; int temp = *input_list.begin();
while (temp <= value) {
temp = *input_list.next(); index++;
}
rv1 = index - 1; rv2 = index; return;
}
This seems not to work, as the member function next() is not existing. However I remember that browsing through a linked list is done by going to the beginning, and moving further to the next element until the a certain point is reached. I have seen that there is a way to get this done by using an interator in a for-loop, but I wonder what's wrong with my approach? I was under the impression that a std::list was a standard implementation of a double-directional linked list, or am I wrong and in that case, what std class is the implementation of a linked list (it does not need to be a double-directional linked list)?
The standard way to iterate through containers is like this:
for(std::list<int>::iterator it = input_list.begin();
it != input_list.end();
it++)
{
....
}
This also works for vectors,maps,deque,etc. The Iterator concept is consistently implemented throughout the STL so it's best to get used to this concepts.
There are also iterator operations like std::distance and std::advance etc. for the different types of iterators (I suggest you read up on them and their advantages/limitations)
If you have C++ 11 available you can also use this syntax (may not be useful for your problem though.)
for(const auto& value : input_list)
{
...
}
This also works throughout the STL container.
This should work for vector, list, deque, and set (assuming the contents are sorted).
template <class T>
void find_values_in_container(const T& container, int value, int &rv1, int &rv2)
{
rv1 = rv2 = 0; // Initialize
if (container.empty() || container.front() >= value)
{
return;
}
for (const auto& v : container)
{
rv2++;
if (v > value)
{
break;
}
rv1++;
}
return;
}
I need to implement the following datastructure for my project. I have a relation of
const MyClass*
to
uint64_t
For every pointer I want to save a counter connected to it, which can be changed over time (in fact only incremented). This would be no problem, I could simply store it in a std::map. The problem is that I need fast access to the pointers which have the highest values.
That is why I came to the conclusion to use a boost::bimap. It is defined is follows for my project:
typedef boost::bimaps::bimap<
boost::bimaps::unordered_set_of< const MyClass* >,
boost::bimaps::multiset_of< uint64_t, std::greater<uint64_t> >
> MyBimap;
MyBimap bimap;
This would work fine, but am I right that I can not modify the uint64_t on pair which were inserted once? The documentation says that multiset_of is constant and therefore I cannot change a value of pair in the bimap.
What can I do? What would be the correct way to change the value of one key in this bimap? Or is there a simpler data structure possible for this problem?
Here's a simple hand-made solution.
Internally it keeps a map to store the counts indexed by object pointer, and a further multi-set of iterators, ordered by descending count of their pointees.
Whenever you modify a count, you must re-index. I have done this piecemeal, but you could do it as a batch update, depending on requirements.
Note that in c++17 there is a proposed splice operation for sets and maps, which would make the re-indexing extremely fast.
#include <map>
#include <set>
#include <vector>
struct MyClass { };
struct store
{
std::uint64_t add_value(MyClass* p, std::uint64_t count = 0)
{
add_index(_map.emplace(p, count).first);
return count;
}
std::uint64_t increment(MyClass* p)
{
auto it = _map.find(p);
if (it == std::end(_map)) {
// in this case, we'll create one - we could throw instead
return add_value(p, 1);
}
else {
remove_index(it);
++it->second;
add_index(it);
return it->second;
}
}
std::uint64_t query(MyClass* p) const {
auto it = _map.find(p);
if (it == std::end(_map)) {
// in this case, we'll create one - we could throw instead
return 0;
}
else {
return it->second;
}
}
std::vector<std::pair<MyClass*, std::uint64_t>> top_n(std::size_t n)
{
std::vector<std::pair<MyClass*, std::uint64_t>> result;
result.reserve(n);
for (auto idx = _value_index.begin(), idx_end = _value_index.end() ;
n && idx != idx_end ;
++idx, --n) {
result.emplace_back((*idx)->first, (*idx)->second);
}
return result;
}
private:
using map_type = std::map<MyClass*, std::uint64_t>;
struct by_count
{
bool operator()(map_type::const_iterator l, map_type::const_iterator r) const {
// note: greater than orders by descending count
return l->second > r->second;
}
};
using value_index_type = std::multiset<map_type::iterator, by_count>;
void add_index(map_type::iterator iter)
{
_value_index.emplace(iter->second, iter);
}
void remove_index(map_type::iterator iter)
{
for(auto range = _value_index.equal_range(iter);
range.first != range.second;
++range.first)
{
if (*range.first == iter) {
_value_index.erase(range.first);
return;
}
}
}
map_type _map;
value_index_type _value_index;
};
I built the following map using a self-defined struct.
#include <iostream>
#include <vector>
#include <map>
struct keys {
int first;
int second;
int third;
};
struct keyCompare
{
bool operator()(const keys& k1, const keys& k2)
{
//return (k1.first<k2.first && k1.second<k2.second && k1.third<k2.third);
return (k1.first<k2.first || k1.second<k2.second || k1.third<k2.third);
//return (k1.first<k2.first || (k1.first==k2.first && k1.second<k2.second) || (k1.first==k2.first
// && k1.second==k2.second && k1.third<k2.third));
}
};
int main()
{
keys mk, mk1;
int len = 4;
//int myints1[9] = {1,2,3,4,5,6, 7,8,9};
int myints1[12] = {1,2,3,4,5,6,1,2,3,1,2,3};
std::vector<int> input1(myints1, myints1+12);
std::map<keys, int, keyCompare> c2int;
for (int i = 0; i < len; i++) {
mk.first = input1[i*3];
mk.second = input1[i*3+1];
mk.third = input1[i*3+2];
c2int[mk] = i;
}
for (int i = 0; i < len;i++) {
mk1.first = input1[i*3];
mk1.second = input1[i*3+1];
mk1.third = input1[i*3+2];
std::cout << "map content " << c2int[mk1] << "\n";
}
return 0;}
The code works as expected for non-repeated keys like {1,2,3,4,5,6,7,8,9}. The return is
map content is 0
map content is 1
map content is 2
but when there are repeated patterns, e.g., keys are {1,2,3,4,5,6,1,2,3}. The print out is
map content is 2
map content is 1
map content is 2
while I was expecting
map content is 0
map content is 1
map content is 0
since key {1,2,3} has already assigned value 0. But the compare function seems modify this key to value 2 instead of 0. I tried different compare function but none of them shows expected output. I think I had missed something in this approach. Can someone explain? Thanks
This comparator is incorrect:
bool operator()(const keys& k1, const keys& k2)
{
return (k1.first<k2.first || k1.second<k2.second || k1.third<k2.third);
}
Consider {1,4,9} vs {2,3,4}. {1,4,9} < {2,3,4} because of the first comparison, but then {2,3,4} < {1,4,9} because of the second! That's clearly not what you intended! Besides, operator< must be asymmetric in order to be a StrictWeakOrdering, which is what is required for std::map.
You have to deal with the keys in order:
bool operator()(const keys& k1, const keys& k2) {
if (k1.first != k2.first) {
return k1.first < k2.first;
}
else if (k1.second != k2.second) {
return k1.second < k2.second;
}
else {
return k1.third < k2.third;
}
}
Your keyCompare is not valid for a std::map.
It needs to return true for (a,b) if a should be ordered before b.
You have written a function that could return true for (a,b) and (b,a). This violates strict weak ordering.
I have the following map:
void groupIntoClasses (vector<FileData>fd, map<int,vector<FileData>> &classes )
{
classes.clear();
for (int i=0; i<fd.size(); i++)
{
string name = fd[i].fileName;
string path = fd[i].filePath;
string hash = fd[i].fileHash;
int size = fd[i].fileSize;
classes[size].push_back( {name,path,size,hash});
}
}
which matches each of my FileData objects according to the field fileSize.
What I want to do now is erase the keys which are associated with only a single value (leave only all the duplicates in the map), but I am having difficulties handling the iterator.
If I have understood you then you can write the following loop to do the task.
for ( auto it = classes.cbegin(); it != classes.cend(); )
{
if ( (*it).second.size() == 1 ) it = classes.erase( it );
else ++it;
}
Maybe something like this:
void eraseSingleEntries(map<int,vector<FileData> > &classes)
{
map<int,vector<FileData> >::iterator i = classes.begin();
while (i!=classes.end()) {
if (i->second.size()==1) {
classes.erase(i++);
}
else {
++i;
}
}
}