在 C++ 中随机排列字符串列表
Shuffle a list of strings in C++
我尝试打乱以下字符串列表:
list<string> l({"10000007", "1", "4", "5", "7", "12", "23", "25", "26", "27", "30", "31", "32", "44", "46", "47", "59", "65", "91"})
我所有的尝试都失败了。这是我最好的尝试之一。
尝试 1
基本上我从这个回答中复制
#include <iostream>
#include <functional>
#include <iterator>
#include <algorithm>
#include <string>
#include <list>
#include <vector>
#include <random>
#include <numeric>
int main() {
std::list<std::string> l({"10000007", "1", "4", "5", "7", "12", "23", "25", "26", "27", "30", "31", "32", "44", "46", "47", "59", "65", "91"});
std::vector<std::reference_wrapper<std::string>> v(l.cbegin(), l.cend());
std::random_device rd;
std::mt19937 generator(rd());
std::shuffle(v.begin(), v.end(), generator);
std::cout << "Original list:\n";
std::copy(l.cbegin(), l.cend(), std::ostream_iterator<std::string>(std::cout, " "));
std::cout << "\nShuffled view:\n";
std::copy(v.cbegin(), v.cend(), std::ostream_iterator<std::string>(std::cout, " "));
}
我有这个错误跟踪:
In file included from /usr/include/c++/7/vector:62:0,
from /usr/include/c++/7/functional:61,
from prueba.cpp:2:
/usr/include/c++/7/bits/stl_construct.h: In instantiation of ‘void std::_Construct(_T1*, _Args&& ...) [with _T1 = std::reference_wrapper<std::__cxx11::basic_string<char> >; _Args = {const std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&}]’:
/usr/include/c++/7/bits/stl_uninitialized.h:83:18: required from ‘static _ForwardIterator std::__uninitialized_copy<_TrivialValueTypes>::__uninit_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = std::_List_const_iterator<std::__cxx11::basic_string<char> >; _ForwardIterator = std::reference_wrapper<std::__cxx11::basic_string<char> >*; bool _TrivialValueTypes = false]’
/usr/include/c++/7/bits/stl_uninitialized.h:134:15: required from ‘_ForwardIterator std::uninitialized_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = std::_List_const_iterator<std::__cxx11::basic_string<char> >; _ForwardIterator = std::reference_wrapper<std::__cxx11::basic_string<char> >*]’
/usr/include/c++/7/bits/stl_uninitialized.h:289:37: required from ‘_ForwardIterator std::__uninitialized_copy_a(_InputIterator, _InputIterator, _ForwardIterator, std::allocator<_Tp>&) [with _InputIterator = std::_List_const_iterator<std::__cxx11::basic_string<char> >; _ForwardIterator = std::reference_wrapper<std::__cxx11::basic_string<char> >*; _Tp = std::reference_wrapper<std::__cxx11::basic_string<char> >]’
/usr/include/c++/7/bits/stl_vector.h:1331:33: required from ‘void std::vector<_Tp, _Alloc>::_M_range_initialize(_ForwardIterator, _ForwardIterator, std::forward_iterator_tag) [with _ForwardIterator = std::_List_const_iterator<std::__cxx11::basic_string<char> >; _Tp = std::reference_wrapper<std::__cxx11::basic_string<char> >; _Alloc = std::allocator<std::reference_wrapper<std::__cxx11::basic_string<char> > >]’
/usr/include/c++/7/bits/stl_vector.h:1299:23: required from ‘void std::vector<_Tp, _Alloc>::_M_initialize_dispatch(_InputIterator, _InputIterator, std::__false_type) [with _InputIterator = std::_List_const_iterator<std::__cxx11::basic_string<char> >; _Tp = std::reference_wrapper<std::__cxx11::basic_string<char> >; _Alloc = std::allocator<std::reference_wrapper<std::__cxx11::basic_string<char> > >]’
/usr/include/c++/7/bits/stl_vector.h:414:26: required from ‘std::vector<_Tp, _Alloc>::vector(_InputIterator, _InputIterator, const allocator_type&) [with _InputIterator = std::_List_const_iterator<std::__cxx11::basic_string<char> >; <template-parameter-2-2> = void; _Tp = std::reference_wrapper<std::__cxx11::basic_string<char> >; _Alloc = std::allocator<std::reference_wrapper<std::__cxx11::basic_string<char> > >; std::vector<_Tp, _Alloc>::allocator_type = std::allocator<std::reference_wrapper<std::__cxx11::basic_string<char> > >]’
prueba.cpp:13:76: required from here
/usr/include/c++/7/bits/stl_construct.h:75:7: error: binding reference of type ‘std::__cxx11::basic_string<char>&’ to ‘const std::__cxx11::basic_string<char>’ discards qualifiers
{ ::new(static_cast<void*>(__p)) _T1(std::forward<_Args>(__args)...); }
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from /usr/include/c++/7/bits/std_function.h:44:0,
from /usr/include/c++/7/functional:58,
from prueba.cpp:2:
/usr/include/c++/7/bits/refwrap.h:334:7: note: initializing argument 1 of ‘std::reference_wrapper<_Tp>::reference_wrapper(_Tp&) [with _Tp = std::__cxx11::basic_string<char>]’
reference_wrapper(_Tp& __indata) noexcept
^~~~~~~~~~~~~~~~~
我试图理解 reference_wrapper 在这个例子中做了什么,但没有成功。
其他尝试
我对 经典 组合的所有尝试也都失败了... random_engine
、shuffle
和 srand
的跟踪错误为数千行数。
为什么不转换为整数?
在我的原始代码中,有两个 .json 文件,jsons 总是有字符串格式的键。我知道我可以将字符串列表转换为整数列表,但问题是我正在创建算法并且有:
- 2000 个列表
- 算法中的 2000 次迭代
- 你必须多次进行这种转换每次迭代(字符串到整数,整数到字符串...)
所以我认为这个选项在计算上会非常昂贵。
提前致谢。
这一行:
std::vector<std::reference_wrapper<std::string>> v(l.cbegin(), l.cend());
您正在使用 const_iterator
构造向量,这会在从 const std::string
构造 reference_wrapper<std::string>
时导致类型不匹配错误。
相反,您需要 non-const 个迭代器,如下所示:
std::vector<std::reference_wrapper<std::string>> v(l.begin(), l.end());
这是 demo。
cbegin()
成员函数 returns a const_iterator
取消引用时 returns a const std::string&
.
将您的 vector
的创建更改为:
std::vector<std::reference_wrapper<const std::string>> v(l.cbegin(), l.cend());
我首先质疑 use/need 是否使用了 std::list
。在这种情况下,您似乎只使用 list
来初始化 vector
。既然如此,你还不如直接初始化并使用vector
:
#include <iostream>
#include <functional>
#include <iterator>
#include <algorithm>
#include <string>
#include <list>
#include <vector>
#include <random>
#include <numeric>
int main() {
std::vector<std::string> v({"10000007", "1", "4", "5", "7", "12", "23", "25", "26", "27", "30", "31", "32", "44", "46", "47", "59", "65", "91"});
std::random_device rd;
std::cout << "Original list:\n";
std::copy(v.cbegin(), v.cend(), std::ostream_iterator<std::string>(std::cout, " "));
std::mt19937 generator(rd());
std::shuffle(v.begin(), v.end(), generator);
std::cout << "\nShuffled view:\n";
std::copy(v.cbegin(), v.cend(), std::ostream_iterator<std::string>(std::cout, " "));
std::cout << "\n";
}
如果你真的坚持使用列表,你可以摆脱引用包装器,它会起作用:
#include <iostream>
#include <functional>
#include <iterator>
#include <algorithm>
#include <string>
#include <list>
#include <vector>
#include <random>
#include <numeric>
int main() {
std::list<std::string> l({"10000007", "1", "4", "5", "7", "12", "23", "25", "26", "27", "30", "31", "32", "44", "46", "47", "59", "65", "91"});
std::vector<std::string> v(l.cbegin(), l.cend());
std::random_device rd;
std::mt19937 generator(rd());
std::shuffle(v.begin(), v.end(), generator);
std::cout << "Original list:\n";
std::copy(l.cbegin(), l.cend(), std::ostream_iterator<std::string>(std::cout, " "));
std::cout << "\nShuffled view:\n";
std::copy(v.cbegin(), v.cend(), std::ostream_iterator<std::string>(std::cout, " "));
}
...但我认为这没有什么意义。
至于存储整数与字符串的开销:只要您使用的是相对较新的编译器,因此其(库的)std::string
实现具有短字符串优化,可能没有两者之间的巨大差异。如果您(可能)需要支持缺乏短字符串优化的旧编译器,这会大大增加您转换和存储 int
s 而不是存储字符串的机会。
问题很简单:如果没有短字符串优化,每个字符串都会导致堆分配来存储实际数据。堆分配可能很容易比与 int 之间的转换慢(但您需要进行测试才能确定)。
对于那些非常关心速度的人来说,理想的做法可能是内存映射整个 json 文件,然后为您关心的值创建 string_view
对象。然后你可以洗牌 string_view
对象,并打印出你认为合适的(或其他)——但所有这些都只包含指向原始数据的指针,所以你不会复制所有底层数据。
我尝试打乱以下字符串列表:
list<string> l({"10000007", "1", "4", "5", "7", "12", "23", "25", "26", "27", "30", "31", "32", "44", "46", "47", "59", "65", "91"})
我所有的尝试都失败了。这是我最好的尝试之一。
尝试 1
基本上我从这个回答中复制
#include <iostream>
#include <functional>
#include <iterator>
#include <algorithm>
#include <string>
#include <list>
#include <vector>
#include <random>
#include <numeric>
int main() {
std::list<std::string> l({"10000007", "1", "4", "5", "7", "12", "23", "25", "26", "27", "30", "31", "32", "44", "46", "47", "59", "65", "91"});
std::vector<std::reference_wrapper<std::string>> v(l.cbegin(), l.cend());
std::random_device rd;
std::mt19937 generator(rd());
std::shuffle(v.begin(), v.end(), generator);
std::cout << "Original list:\n";
std::copy(l.cbegin(), l.cend(), std::ostream_iterator<std::string>(std::cout, " "));
std::cout << "\nShuffled view:\n";
std::copy(v.cbegin(), v.cend(), std::ostream_iterator<std::string>(std::cout, " "));
}
我有这个错误跟踪:
In file included from /usr/include/c++/7/vector:62:0,
from /usr/include/c++/7/functional:61,
from prueba.cpp:2:
/usr/include/c++/7/bits/stl_construct.h: In instantiation of ‘void std::_Construct(_T1*, _Args&& ...) [with _T1 = std::reference_wrapper<std::__cxx11::basic_string<char> >; _Args = {const std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&}]’:
/usr/include/c++/7/bits/stl_uninitialized.h:83:18: required from ‘static _ForwardIterator std::__uninitialized_copy<_TrivialValueTypes>::__uninit_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = std::_List_const_iterator<std::__cxx11::basic_string<char> >; _ForwardIterator = std::reference_wrapper<std::__cxx11::basic_string<char> >*; bool _TrivialValueTypes = false]’
/usr/include/c++/7/bits/stl_uninitialized.h:134:15: required from ‘_ForwardIterator std::uninitialized_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = std::_List_const_iterator<std::__cxx11::basic_string<char> >; _ForwardIterator = std::reference_wrapper<std::__cxx11::basic_string<char> >*]’
/usr/include/c++/7/bits/stl_uninitialized.h:289:37: required from ‘_ForwardIterator std::__uninitialized_copy_a(_InputIterator, _InputIterator, _ForwardIterator, std::allocator<_Tp>&) [with _InputIterator = std::_List_const_iterator<std::__cxx11::basic_string<char> >; _ForwardIterator = std::reference_wrapper<std::__cxx11::basic_string<char> >*; _Tp = std::reference_wrapper<std::__cxx11::basic_string<char> >]’
/usr/include/c++/7/bits/stl_vector.h:1331:33: required from ‘void std::vector<_Tp, _Alloc>::_M_range_initialize(_ForwardIterator, _ForwardIterator, std::forward_iterator_tag) [with _ForwardIterator = std::_List_const_iterator<std::__cxx11::basic_string<char> >; _Tp = std::reference_wrapper<std::__cxx11::basic_string<char> >; _Alloc = std::allocator<std::reference_wrapper<std::__cxx11::basic_string<char> > >]’
/usr/include/c++/7/bits/stl_vector.h:1299:23: required from ‘void std::vector<_Tp, _Alloc>::_M_initialize_dispatch(_InputIterator, _InputIterator, std::__false_type) [with _InputIterator = std::_List_const_iterator<std::__cxx11::basic_string<char> >; _Tp = std::reference_wrapper<std::__cxx11::basic_string<char> >; _Alloc = std::allocator<std::reference_wrapper<std::__cxx11::basic_string<char> > >]’
/usr/include/c++/7/bits/stl_vector.h:414:26: required from ‘std::vector<_Tp, _Alloc>::vector(_InputIterator, _InputIterator, const allocator_type&) [with _InputIterator = std::_List_const_iterator<std::__cxx11::basic_string<char> >; <template-parameter-2-2> = void; _Tp = std::reference_wrapper<std::__cxx11::basic_string<char> >; _Alloc = std::allocator<std::reference_wrapper<std::__cxx11::basic_string<char> > >; std::vector<_Tp, _Alloc>::allocator_type = std::allocator<std::reference_wrapper<std::__cxx11::basic_string<char> > >]’
prueba.cpp:13:76: required from here
/usr/include/c++/7/bits/stl_construct.h:75:7: error: binding reference of type ‘std::__cxx11::basic_string<char>&’ to ‘const std::__cxx11::basic_string<char>’ discards qualifiers
{ ::new(static_cast<void*>(__p)) _T1(std::forward<_Args>(__args)...); }
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from /usr/include/c++/7/bits/std_function.h:44:0,
from /usr/include/c++/7/functional:58,
from prueba.cpp:2:
/usr/include/c++/7/bits/refwrap.h:334:7: note: initializing argument 1 of ‘std::reference_wrapper<_Tp>::reference_wrapper(_Tp&) [with _Tp = std::__cxx11::basic_string<char>]’
reference_wrapper(_Tp& __indata) noexcept
^~~~~~~~~~~~~~~~~
我试图理解 reference_wrapper 在这个例子中做了什么,但没有成功。
其他尝试
我对 经典 组合的所有尝试也都失败了... random_engine
、shuffle
和 srand
的跟踪错误为数千行数。
为什么不转换为整数?
在我的原始代码中,有两个 .json 文件,jsons 总是有字符串格式的键。我知道我可以将字符串列表转换为整数列表,但问题是我正在创建算法并且有:
- 2000 个列表
- 算法中的 2000 次迭代
- 你必须多次进行这种转换每次迭代(字符串到整数,整数到字符串...)
所以我认为这个选项在计算上会非常昂贵。
提前致谢。
这一行:
std::vector<std::reference_wrapper<std::string>> v(l.cbegin(), l.cend());
您正在使用 const_iterator
构造向量,这会在从 const std::string
构造 reference_wrapper<std::string>
时导致类型不匹配错误。
相反,您需要 non-const 个迭代器,如下所示:
std::vector<std::reference_wrapper<std::string>> v(l.begin(), l.end());
这是 demo。
cbegin()
成员函数 returns a const_iterator
取消引用时 returns a const std::string&
.
将您的 vector
的创建更改为:
std::vector<std::reference_wrapper<const std::string>> v(l.cbegin(), l.cend());
我首先质疑 use/need 是否使用了 std::list
。在这种情况下,您似乎只使用 list
来初始化 vector
。既然如此,你还不如直接初始化并使用vector
:
#include <iostream>
#include <functional>
#include <iterator>
#include <algorithm>
#include <string>
#include <list>
#include <vector>
#include <random>
#include <numeric>
int main() {
std::vector<std::string> v({"10000007", "1", "4", "5", "7", "12", "23", "25", "26", "27", "30", "31", "32", "44", "46", "47", "59", "65", "91"});
std::random_device rd;
std::cout << "Original list:\n";
std::copy(v.cbegin(), v.cend(), std::ostream_iterator<std::string>(std::cout, " "));
std::mt19937 generator(rd());
std::shuffle(v.begin(), v.end(), generator);
std::cout << "\nShuffled view:\n";
std::copy(v.cbegin(), v.cend(), std::ostream_iterator<std::string>(std::cout, " "));
std::cout << "\n";
}
如果你真的坚持使用列表,你可以摆脱引用包装器,它会起作用:
#include <iostream>
#include <functional>
#include <iterator>
#include <algorithm>
#include <string>
#include <list>
#include <vector>
#include <random>
#include <numeric>
int main() {
std::list<std::string> l({"10000007", "1", "4", "5", "7", "12", "23", "25", "26", "27", "30", "31", "32", "44", "46", "47", "59", "65", "91"});
std::vector<std::string> v(l.cbegin(), l.cend());
std::random_device rd;
std::mt19937 generator(rd());
std::shuffle(v.begin(), v.end(), generator);
std::cout << "Original list:\n";
std::copy(l.cbegin(), l.cend(), std::ostream_iterator<std::string>(std::cout, " "));
std::cout << "\nShuffled view:\n";
std::copy(v.cbegin(), v.cend(), std::ostream_iterator<std::string>(std::cout, " "));
}
...但我认为这没有什么意义。
至于存储整数与字符串的开销:只要您使用的是相对较新的编译器,因此其(库的)std::string
实现具有短字符串优化,可能没有两者之间的巨大差异。如果您(可能)需要支持缺乏短字符串优化的旧编译器,这会大大增加您转换和存储 int
s 而不是存储字符串的机会。
问题很简单:如果没有短字符串优化,每个字符串都会导致堆分配来存储实际数据。堆分配可能很容易比与 int 之间的转换慢(但您需要进行测试才能确定)。
对于那些非常关心速度的人来说,理想的做法可能是内存映射整个 json 文件,然后为您关心的值创建 string_view
对象。然后你可以洗牌 string_view
对象,并打印出你认为合适的(或其他)——但所有这些都只包含指向原始数据的指针,所以你不会复制所有底层数据。