【问题标题】:Filling an array with random numbers from 1 to 10^10 in C or C++在 C 或 C++ 中用 1 到 10^10 的随机数填充数组
【发布时间】:2011-06-09 17:03:23
【问题描述】:

我的部分作业基于一个数组(其大小由用户指定),其中包含从 1 到 10^10 的随机数。然后我们必须找到数组的第 k 个较小的数字。这是我尝试过的:

#include <cstdlib>
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <time.h>

using namespace std;

void swap(int *x,int *y)
{
    int temp;
    temp = *x;
    *x = *y;
    *y = temp;
}

int choose_pivot(int i,int j )
{
    return((i+j) /2);
}

// Print array
void printarr(int arr[],int n)
{
    int i;
    for(i=0;i<n;i++)
        printf("%d\t",arr[i]);
}

// Find algorithm
int find1(int arr[],int left,int right,int k)
{
    int i,j,pivot;
    if (left==right)
        return arr[left];
    else
    {
        i=left;
        j=right+1;
        pivot= arr[left];
        do
        {
            do {
                i=i+1;
            } while (arr[i]>=pivot);
            do {
                j =j-1;
            } while (arr[j]<=pivot);
            if (i<j)
                swap(arr[i],arr[j]);
        } while (j<=i);
    }
    swap(arr[left],arr[j]);
    if (k==j)
        return arr[j];
    else if (k<j)
        find1(arr,left,j-1,k);
    else 
        find1(arr,j+1,right,k-j);
}

int main(int argc, char *argv[])
{
    srand(time(NULL));
    int n,i,fi,k;
    printf("Give array's size:\n");
    scanf("%d",&n);
    int pin[n];
    for (i=0;i<n;i++)
        pin[i]=((rand()*rand()) % 1000000000) +1;
    printf("Give k: \n");
    scanf("%d",&k);
    printf("The array contains the following numbers:\n\n");
    printarr(pin,n);
    fi=find1(pin,0,n-1,k);//find the k-th smallest number in the array
    printf("The k-th smallest number is: %d",fi);

    system("PAUSE");
}

正如您所见,10^10 是一个非常大的值,我做了一些其他的事情来用随机数填充数组。这是正确的吗?还有什么我可以做的吗? 我的第二个问题是关于查找算法的。它不起作用。谁能帮我解决这些问题?非常感谢

【问题讨论】:

  • 你真的应该使用 rand()*rand()。这没有均匀分布。
  • @Kieth,你的意思是不应该,对吧?
  • 我们真的不应该做一些孩子的作业,尤其是当他懒得尝试编译的时候。
  • @Tim 我的大多数前学校同事只是在编写完代码后才编译他们的代码(他们中的大多数也只是在那时才保存......)。我认为这是刚开始学习编程的人的共同特征。然而,这并不会让他变得懒惰。正如你所看到的,他试了一下,并带着一些关于 他的 代码的问题来到这里。另一件事是,通常,如果一个人将他的代码识别为作业,那么回答他的人只会给他提示,所以没有人在做作业。
  • @Alin 我完全可以帮助那些被困在家庭作业中的人——我只想强调我们不应该提供答案。我认为发布“这是我的编译错误,但我不明白”是完全有效的。然而,pr_prog84 只是发布了他的代码并说“它不起作用”。这对我来说是一个危险信号。

标签: c++ c arrays random


【解决方案1】:

rand()*rand() 与单个rand() 有很大不同,它降低了随机性 并改变了它的分布。请参阅this question 以获得更深入的解释。

另外,一个整数通常是 4 个字节。如果它没有签名,它可以包含一个大到2^31(20 亿多)或2^32(40 亿多)的值。您可以查看它可以包含的最大数量,检查limits.h 中定义的INT_MAX 宏。 10^10 是 100 亿,它不适合整数,你必须使用更大的类型(long long 通常是 64 字节,因此比你需要的多)。

rand 同样,返回最大为RAND_MAX 的数字,并且由于它返回一个int,它不会大于INT_MAX。您应该使用其他方式来生成像10^10 这样大的数字。

如果您不关心随机性和随机数分布,您可以对n 随机数(由rand 获得)求和,以便n=10^10 / RAND_MAX。

【讨论】:

  • 移位和 OR-ing 将这些片段组合在一起比将随机数相加更有效,并且还会产生更线性的分布。将它们加在一起得到正态分布。
  • 感谢蒂姆的回复。我整天都在努力找出 find 出了什么问题..
  • @peoro "你可以对 n 个随机数求和" 只要你不关心分布,你最好乘以 10^10 / RAND_MAX。 @Ben 添加它们会得到一个在 n 接近无穷大时接近正态分布的分布。您对正在发生的事情有所了解,但它并不完全正确。 @Oli 随机性不会改变,只会改变分布。有区别。
  • @Alin:假设我的理解不正确是很危险的。两点:(1)评论空间太小,无法解释更弱和更强的中心极限定理,(2)这些是整数,所以是的,它只是正态分布的近似值,但不是因为我们有有限的加法项,而是因为它是离散分布,而正态分布是连续的。 N = 10**10 / RAND_MAX 有几百万,我觉得很安全,声称得到的分布与这个范围内的整数尽可能接近正常。
  • @Alin:线性分布的总和是 N = 几十个正态分布的非常好的近似值。
【解决方案2】:

如果您仔细查看 1010,您会发现这是一个相当大的整数限制。我对此的看法是生成每个数字,一次一个数字,忽略无关紧要的零。此时,您将有一个介于 0 和 1010-1 之间的数字。您剩下要做的就是添加一个 1。

至于random()*random(),就是this other question的确切话题。

阿林

【讨论】:

  • 这将在 10 步中完成,使用二进制数字(一次 7 位)可以在 5 内完成。
  • @Ben Voigt 我不熟悉 C 或 C++,因此无法对您的解决方案发表意见。我只能说,我发布的是一种通用算法方法,可以在大多数编程语言中以类似的方式使用,并且可以轻松支持更广泛的范围(比如 1 到 10^100)。
【解决方案3】:

问题 #1,int 只能容纳大小为 2^31 的数字。您需要为您的引脚阵列提供稍大的替代方案。

另外,将你的两个随机数相乘并没有多大作用 - 除非可能使数字不那么随机。

接下来,您不能使用用户的输入在堆栈上动态创建数组。这将需要一个新的解决方案来为你创建一个数组。

【讨论】:

    【解决方案4】:

    您忘记了退货声明。在find1 结尾处你应该这样做:

    if (k==j)
        return arr[j];
    else if (k<j)
        return find1(arr,left,j-1,k);
    else 
        return find1(arr,j+1,right,k-j);
    }
    

    【讨论】:

      【解决方案5】:
      long long get_big_rand()
      {
      
          long long result;
          do {
              result = (rand() & 0x3ff);
              result <<= 12;
              result |= (rand() & 0xfff);
              result <<= 12;
              result |= (rand() & 0xfff);
          } while (++result > 10000000000ULL);
          return result;
      }
      

      【讨论】:

      • +1:这是迄今为止最好的答案。如果您对RAND_MAX 的值有信心,大概可以一次生成更多位。
      • @Oli:完全正确。需要 34 位,可以是 5 步,每步 7,4 步,每步 9,或 3 步,每步 12,因为RAND_MAX 的最常见的实际值可能是 215-1。似乎标准保证至少 215-1,我会更新我的答案。
      【解决方案6】:

      rand()*rand() 不会为您做任何事情。它不会像你想象的那样扩展,它确实改变了分布。其实

      double norm_rand(){
          double r=0;
          for(unsigned i=0;i!=12;++i)
              r+=rand()/static_cast<double>(RAND_MAX);
          return (r/12)-6;
      }
      

      是模拟均值为0,方差为1的正态分布的常用方法;

      获得大随机数的最佳方法是使用随机数设备,例如 /dev/urandom 或 RtlGenRandom。 即

      typedef unsigned long long big_type;
      std::vector<double> rnums;
      std::vector<big_type> buf(numtoread);
              std::ifstream rnds("/dev/urandom"); 
      rnds.read(reinterpret_cast<char*>(&buf[0],buf.size()*sizeof(big_type));
      std::transform(buf.begin(),buf.end(),std::back_inserter(rnums),
           [](big_type const& i){
              return (i*100000000000.)/(std::numeric_limits<big_type>::max());
           });
      

      冒着为你做功课的风险,一个完全不同的方法是使用 C++ 附带的库。

      #include <cassert>
      #include <sstream>
      #ifndef _MSC_VER  //then assume Linux
      #include <tr1/random>
      #else
      #include <random>
      #endif
      #include <boost/lexical_cast.hpp>
      #include <algorithm>
      #include <iterator>
      #include <iostream>
      int main(int argc, char** argv)
      {
          assert(argc==3);
          unsigned const numentries=boost::lexical_cast<unsigned>(argv[1]);
          unsigned const k=boost::lexical_cast<unsigned>(argv[2]);
          std::cout<<" finding "<<k<<"th of "<< numentries<<" entries\n";
          assert(k<=numentries);
          std::vector<double> nums(numentries);
          std::tr1::uniform_real<> rng(0.,10000000000.);
          std::tr1::minstd_rand generator(42u);
          std::tr1::variate_generator<std::tr1::minstd_rand, std::tr1::uniform_real<> >
                  uni(generator, rng);
          std::generate_n(nums.begin(),nums.size(),uni);
          std::cout<<" Generated:\t ";
          std::copy(nums.begin(),nums.end(),std::ostream_iterator<double>(std::cout,"\t"));
          std::sort(nums.begin(),nums.end());
          std::cout<<"\n The "<<k<<"th smallest entry is "<<nums[k]<<"\n";
          return 0;
      }
      

      (如果你在课堂上只是要求制作一组兰特数字并且你把它交出来,他们可能会让你失败) 我在实践中所做的是将这两种方法结合起来。这用于代替上面使用的线性相关 rng(minstd_rand):

      template<typename bigtype=unsigned>
      struct randeng {
          typedef bigtype result_type;
          randeng(unsigned x) :
              m_samplesrequired(x), m_samples(x), m_lastused() {
              std::ifstream rand;
              rand.open("/dev/urandom");
              assert(rand);
              rand.read(reinterpret_cast<char*> (&*(m_samples.begin())),
                      m_samplesrequired * sizeof(unsigned));
          }
          result_type operator()() const {
              assert(m_lastused<m_samplesrequired);
              return m_samples[m_lastused++];
          }
          result_type max() const {
              return std::numeric_limits<result_type>::max();
          }
          result_type min() const {
              return 0;
          }
          unsigned m_samplesrequired;
          std::vector<result_type> m_samples;
          mutable unsigned m_lastused;
      };
      

      这似乎总是能产生更好的结果。

      【讨论】:

        猜你喜欢
        • 2013-07-24
        • 1970-01-01
        • 2012-08-21
        • 2015-12-26
        • 2021-03-14
        • 2018-02-14
        • 2011-04-22
        • 2017-04-21
        • 2013-07-28
        相关资源
        最近更新 更多