Мне удалось реализовать функцию Shuffle для Fisher-Yates для списков python в качестве упражнения для привыкания к расширению python. Он отлично работает для относительно небольших списков, если я не запускаю функцию несколько раз.Расширение Python создает недопустимые указатели при манипулировании большими списками
Всякий раз, когда размер списка переходит около 100, я получаю все виды проблем с памятью:
>>>import evosutil
>>> a=[i for i in range(100)]
>>> evosutil.shuffle(a)
>>> a
[52, 66, 0, 58, 41, 18, 50, 37, 81, 43, 74, 49, 90, 20, 63, 32, 89, 60, 2, 44, 3, 80, 15, 24, 22, 69, 86, 31, 56, 68, 34, 13, 38, 26, 14, 91, 73, 79, 39, 65, 5, 75, 84, 55, 7, 53, 93, 42, 40, 9, 51, 82, 29, 30, 99, 64, 33, 97, 27, 11, 6, 67, 16, 94, 95, 62, 57, 17, 78, 77, 71, 98, 72, 8, 88, 36, 85, 59, 21, 96, 23, 46, 10, 12, 48, 83, 4, 92, 45, 54, 1, 25, 19, 70, 35, 61, 47, 28, 87, 76]
>>> (Ctrl-D)
*** Error in `python3': free(): invalid next size (fast): 0x083fe680 ***
Или, при попытке работать в списке 1000 элементов:
*** Error in `python3': munmap_chunk(): invalid pointer: 0x083ff0e0 ***
Или,
Segmentation fault (core dumped)
Вот мой код модуля, который производит ошибку:
inline void _List_SwapItems(PyObject* list, Py_ssize_t i1, Py_ssize_t i2){
PyObject* tmp=PyList_GetItem(list, i2);
PyList_SetItem(list, i2, PyList_GetItem(list, i1));
PyList_SetItem(list, i1, tmp);
}
//Naive Fisher–Yates shuffle
static PyObject* shuffle(PyObject* self, PyObject* args){
PyObject* list;
PyArg_ParseTuple(args,"O", &list);
unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();
std::minstd_rand0 rand(seed);
Py_ssize_t size = PyList_Size(list);
for(int i=0; i<size;++i){
int randIndex = rand()%size;
_List_SwapItems(list, randIndex, i);
}
Py_RETURN_NONE;
}
Мне кажется, что я смогу решить это либо с помощью free(), либо с помощью Py_DECREF(), но я не вижу, где. Я не думаю, что создаю какие-то объекты, просто перемещая их. Так где же проблема памяти?
Это не перетасовка Fisher-Yates, вы не должны выберите случайный элемент во всем наборе, только между курсором (исключенным) и концом списка. С помощью того, что у вас есть, вы можете поменять элемент с собой, который _might_ выполняет смешные вещи (но я вообще не знаком с API-интерфейсом python, так что ...) – Mat
@Mat Ах, это правда. Я слишком быстро читаю псевдокод. Однако я не думаю, что это делает какие-либо функциональные различия в этом случае. – SimLeek
Удивительно, но это имеет большое значение. См. Http://blog.codinghorror.com/the-danger-of-naivete/ – Mat