在C中随机播放通用数组

时间:2014-04-26 14:29:14

标签: c arrays shuffle

我有一个指向结构的指针数组。

我尝试使用这个问题的第一个答案来尝试解决这个问题,但我遇到了一个段错误:

Shuffle array in C

尝试此代码后,我尝试使用此功能进行改组:

static void shuffle(void *array, size_t n, size_t size) {
    void * aux;
    aux = malloc (size);

    if (n > 1) {
        size_t i;
        for (i = 0; i < n - 1; ++i) {

            size_t j = i + rand() / (RAND_MAX / (n - i) + 1);

            memcpy(aux, array[j], size);
            memcpy(array[j], array[i], size);
            memcpy(array[i], aux, size);
        }
    }
}

但是我收到了以下错误:

warning: dereferencing 'void *' pointer [enable by default]
error: invalid use of void expression

我收到了这个警告和错误3次:每个memcpy()一个。

修改

我更改了代码,因为我试图使用这个新代码而不是旧代码。

4 个答案:

答案 0 :(得分:4)

你不能取消引用void*也不能在C中对它进行算术运算,因为它实际上是#34;指向未知类型的对象&#34;,所以编译器不知道该做什么(有些编译器)算术作为一个扩展,好像它是char*,但不要依赖它。)

因此:

void*投射到char*并改为使用它们。不要忘记为指针算法做自己的缩放 此外,memcpy应该获得指针,而不是指向的对象,因此请使用+,而不是[]。 旁白:请避免内存泄漏,free您分配的任何内存 最后,请注意random()可能同时具有微小范围和不良统计属性 最后+ 1:chux answer dissects the range-mapping used in this case,显示它完成得有多糟糕。

How to Debug Small Programs
C99 with Technical corrigenda TC1, TC2, and TC3 included

答案 1 :(得分:3)

除了@Deduplicator好建议外,还需要按j缩放size偏移量。

此代码也会更改j计算。我认为你所拥有的东西不会很好。

#include <stdlib.h>
static void shuffle(void *array, size_t n, size_t size) {
  // This if() is not needed functionally, but left per OP's style
  if (n > 1) {
    char *carray = array;
    void * aux;
    aux = malloc(size);
    size_t i;
    for (i = 1; i < n; ++i) {
      size_t j = rand() % (i + 1); 
      j *= size;
      memcpy(aux, &carray[j], size);
      memcpy(&carray[j], &carray[i*size], size);
      memcpy(&carray[i*size], aux, size);
    }
    free(aux);
  }
}

澄清size_t j = i + rand() / (RAND_MAX / (n - i) + 1);

的弱点

在公式中将rand()替换为值0RAND_MAX将生成样本分布。示例(i = 0,n = 30265,RAND_MAX = 2147483647)生成值j = 0 to 30263,每次70957次,j = 30264 = 41000.这是一个不好的情况,可能不是最坏的情况。

使用size_t j = i + r%(n-i);生成几乎统一结果。

示例(i = 0,n = 30265,RAND_MAX = 2147483647)生成值j = 0 to 307,每次生成70957次,j = 308 to 30264 = 70956次。存在补偿这种小偏差的方法。许多SO帖子。

注意:这些方法中的任何一种都无法修复穷人rand()中的继承弱点,但在尝试使用0到{{1的子范围时,方法选择会使事情明显变得更糟}}

[编辑]

孔显示为RAND_MAX

关于size_t j = i + rand() / (RAND_MAX / (n - i) + 1);n > sqrt(RAND_MAX)的某处,将不会生成i=0附近的值。示例:n不生成值104638到104642。

答案 2 :(得分:1)

  1. 确保您实际将其编译为C代码(C和C ++在类型转换方面有所不同)
  2. 将指针传递给数组中的元素而不是取消引用它们:array[i] - &gt; &array[i]
  3. sizeof(void)无法保证定义,请使用char
  4. 添加一些断言来检查memcpy()的参数。可以使用modulo来钳制i和j,尽管这通常不会导致良好的随机数分布。
  5. static void shuffle(char *array, size_t n, size_t size)
    {
        char* aux;
        aux = malloc(size);
    
        if (n > 1)
        {
            size_t i;
            for (i = 0; i < n - 1; ++i)
            {
    
                size_t j = i + rand() / (RAND_MAX / (n - i) + 1);
    
                assert(j + size <= ???);
                assert(i + size <= ???);
    
                memcpy(aux, &array[j], size);
                memcpy(&array[j], &array[i], size);
                memcpy(&array[i], aux, size);
            }
        }
    }
    

答案 3 :(得分:1)

这是另一种选择:

void shuffle(void *arr, size_t n, size_t size) {
  char *tmp;
  char *array;
  int i, r;
  array = (char *)arr;
  tmp = (char *)malloc(size);
  for (i = n-1; i >= 0; --i) {
    r = rand() % (i+1);
    // swap 
    memcpy(tmp, &array[size*r], size);
    memcpy(&array[size*r], &array[size*i], size);
    memcpy(&array[size*i], tmp, size);
  }
  free(tmp);
}

迄今为止最简单的。请注意,无需检查n=1

如果您真的想使用size_t,请稍微修改一下代码:

void shuffle(void *arr, size_t n, size_t size) {
  char *tmp;
  char *array;
  size_t i, r;
  array = (char *)arr;
  tmp = (char *)malloc(size);
  for (i = n-1; i+1 > 0; --i) {
    r = rand() % (i+1);
    // swap 
    memcpy(tmp, &array[size*r], size);
    memcpy(&array[size*r], &array[size*i], size);
    memcpy(&array[size*i], tmp, size);
  }
  free(tmp);
}

感谢评论者指出使用int的问题。 (上面的代码可能很明显 - 这是随机地均匀地返回数组的排列,即以概率1/n!返回数组的每个排列。这对于{{3}来说是一个有趣的练习。 }。)