2013-06-02 39 views
14

Tôi có một ứng dụng mà nó trở nên cực kỳ đáng chú ý nếu chương trình của tôi sử dụng RNG có mẫu dựa trên hạt giống của nó, vì nó xây dựng phong cảnh dựa trên tọa độ x của cảnh quan. Trong khi Random hoạt động tốt nếu bạn đang gọi Next() mỗi lần, tôi cần có thể có cùng một đầu ra mỗi khi tôi sử dụng cùng một đầu vào và do đó không thể dựa vào Next(). Thay vào đó, tôi cố gắng chỉ đơn giản là tạo một Random mới mỗi lần với hạt giống đầu vào. Không phải là một ý tưởng hay, tôi biết, và nó cho thấy. Các mô hình là cực kỳ rõ ràng, với xen kẽ các giá trị cao và thấp, và một xu hướng tổng thể đáng chú ý trên toàn bộ cảnh quan. Tôi không muốn làm cho máy phát điện mới mỗi lần, nhưng ngay cả như vậy, tôi nhìn vào an ninh mã hóa RandomNumberGenerator để xem nếu tôi ít nhất có thể sử dụng nó tạm thời. Theo dự kiến, mặc dù, tôi không thể hạt giống nó, để lại cho tôi mà không có bất kỳ loại sản lượng tái sản xuất (mà là thay vì điểm của RandomNumberGenerator).Số ngẫu nhiên từ hạt giống

Tóm lại, cả hai RNG phổ biến đều không phù hợp với mục đích của tôi. Tôi cần để có thể đưa vào một số và trả lại một số ngẫu nhiên dựa trên giá trị đó mà không có các mẫu đáng chú ý trong đầu ra. Có cách nào khác để sử dụng hai điều trên, hoặc là có một thứ ba mà tôi chưa từng sử dụng trước đó sẽ phù hợp hơn với mục đích của tôi?

Để rõ ràng, phương pháp Tôi đang cố gắng để viết trông giống như vậy:

public int RandomInt(int input) 
{ 
    int randomOutput; 
    //Be random 
    return randomOutput; 
} 

Điều đó sẽ trả về giá trị giống nhau mỗi khi cùng input được đưa ra.

+0

Xin lỗi, nhưng "đầu vào" bạn đang đề cập đến là gì? – aquaraga

+0

Đầu vào là một int. Tôi đã chỉnh sửa bài đăng để bao gồm những gì tôi đang cố gắng viết. –

+0

bạn nói rằng sử dụng ngẫu nhiên với hạt giống, cho xen kẽ các giá trị cao và thấp? thú vị, bạn có thể sử dụng một số toán học để làm mịn kết quả hoặc xác định một phạm vi cho ngẫu nhiên, vì vậy sự khác biệt sẽ không lớn như vậy – Mzf

Trả lời

14

A Mersenne Twistercó thể cho kết quả tốt hơn.

Dưới đây là một việc thực hiện mẫu mà bạn có thể thử ra khá nhanh chóng:

using System; 

namespace Random 
{ 
    /* C# Version Copyright (C) 2001 Akihilo Kramot (Takel).  */ 
    /* C# porting from a C-program for MT19937, originaly coded by */ 
    /* Takuji Nishimura, considering the suggestions by   */ 
    /* Topher Cooper and Marc Rieffel in July-Aug. 1997.   */ 
    /* This library is free software under the Artistic license: */ 
    /*                */ 
    /* You can find the original C-program at      */ 
    /* http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html */ 
    /*                */ 

    /// <summary> 
    /// Implements a Mersenne Twister Random Number Generator. This class provides the same interface 
    /// as the standard System.Random number generator, plus some additional functions. 
    /// </summary> 

    public class MersenneTwister: System.Random 
    { 
     /* Period parameters */ 
     private const int N = 624; 
     private const int M = 397; 
     private const uint MATRIX_A = 0x9908b0df; /* constant vector a */ 
     private const uint UPPER_MASK = 0x80000000; /* most significant w-r bits */ 
     private const uint LOWER_MASK = 0x7fffffff; /* least significant r bits */ 

     /* Tempering parameters */ 
     private const uint TEMPERING_MASK_B = 0x9d2c5680; 
     private const uint TEMPERING_MASK_C = 0xefc60000; 

     private static uint TEMPERING_SHIFT_U(uint y) { return (y >> 11); } 
     private static uint TEMPERING_SHIFT_S(uint y) { return (y << 7); } 
     private static uint TEMPERING_SHIFT_T(uint y) { return (y << 15); } 
     private static uint TEMPERING_SHIFT_L(uint y) { return (y >> 18); } 

     private uint[] mt = new uint[N]; /* the array for the state vector */ 

     private uint seed_; 
     private short mti; 

     private static uint[] mag01 = { 0x0, MATRIX_A }; 

     /// <summary> 
     /// Create a twister with the specified seed. All sequences started with the same seed will contain 
     /// the same random numbers in the same order. 
     /// </summary> 
     /// <param name="seed">The seed with which to start the twister.</param> 

     public MersenneTwister(uint seed) 
     { 
      Seed = seed; 
     } 


     /// <summary> 
     /// Create a twister seeded from the system clock to make it as random as possible. 
     /// </summary> 

     public MersenneTwister() 
      : this(((uint) DateTime.Now.Ticks)) // A random initial seed is used. 
     { 
     } 


     /// <summary> 
     /// The seed that was used to start the random number generator. 
     /// Setting the seed resets the random number generator with the new seed. 
     /// All sequences started with the same seed will contain the same random numbers in the same order. 
     /// </summary> 

     public uint Seed 
     { 
      set 
      { 
       seed_ = value; 

       /* setting initial seeds to mt[N] using   */ 
       /* the generator Line 25 of Table 1 in   */ 
       /* [KNUTH 1981, The Art of Computer Programming */ 
       /* Vol. 2 (2nd Ed.), pp102]     */ 

       mt[0] = seed_ & 0xffffffffU; 
       for (mti = 1; mti < N; mti++) 
       { 
        mt[mti] = (69069 * mt[mti - 1]) & 0xffffffffU; 
       } 
      } 

      get 
      { 
       return seed_; 
      } 
     } 


     /// <summary> 
     /// Generate a random uint. 
     /// </summary> 
     /// <returns>A random uint.</returns> 

     protected uint GenerateUInt() 
     { 
      uint y; 

      /* mag01[x] = x * MATRIX_A for x=0,1 */ 

      if (mti >= N) /* generate N words at one time */ 
      { 
       short kk; 

       for (kk = 0; kk < N - M; kk++) 
       { 
        y = (mt[kk] & UPPER_MASK) | (mt[kk + 1] & LOWER_MASK); 
        mt[kk] = mt[kk + M]^(y >> 1)^mag01[y & 0x1]; 
       } 

       for (; kk < N - 1; kk++) 
       { 
        y = (mt[kk] & UPPER_MASK) | (mt[kk + 1] & LOWER_MASK); 
        mt[kk] = mt[kk + (M - N)]^(y >> 1)^mag01[y & 0x1]; 
       } 

       y = (mt[N - 1] & UPPER_MASK) | (mt[0] & LOWER_MASK); 
       mt[N - 1] = mt[M - 1]^(y >> 1)^mag01[y & 0x1]; 

       mti = 0; 
      } 

      y = mt[mti++]; 
      y ^= TEMPERING_SHIFT_U(y); 
      y ^= TEMPERING_SHIFT_S(y) & TEMPERING_MASK_B; 
      y ^= TEMPERING_SHIFT_T(y) & TEMPERING_MASK_C; 
      y ^= TEMPERING_SHIFT_L(y); 

      return y; 
     } 


     /// <summary> 
     /// Returns the next uint in the random sequence. 
     /// </summary> 
     /// <returns>The next uint in the random sequence.</returns> 

     public virtual uint NextUInt() 
     { 
      return this.GenerateUInt(); 
     } 


     /// <summary> 
     /// Returns a random number between 0 and a specified maximum. 
     /// </summary> 
     /// <param name="maxValue">The upper bound of the random number to be generated. maxValue must be greater than or equal to zero.</param> 
     /// <returns>A 32-bit unsigned integer greater than or equal to zero, and less than maxValue; that is, the range of return values includes zero but not MaxValue.</returns> 

     public virtual uint NextUInt(uint maxValue) 
     { 
      return (uint) (this.GenerateUInt()/((double) uint.MaxValue/maxValue)); 
     } 


     /// <summary> 
     /// Returns an unsigned random number from a specified range. 
     /// </summary> 
     /// <param name="minValue">The lower bound of the random number returned.</param> 
     /// <param name="maxValue">The upper bound of the random number returned. maxValue must be greater than or equal to minValue.</param> 
     /// <returns>A 32-bit signed integer greater than or equal to minValue and less than maxValue; 
     /// that is, the range of return values includes minValue but not MaxValue. 
     /// If minValue equals maxValue, minValue is returned.</returns> 

     public virtual uint NextUInt(uint minValue, uint maxValue) /* throws ArgumentOutOfRangeException */ 
     { 
      if (minValue >= maxValue) 
      { 
       if (minValue == maxValue) 
       { 
        return minValue; 
       } 
       else 
       { 
        throw new ArgumentOutOfRangeException("minValue", "NextUInt() called with minValue >= maxValue"); 
       } 
      } 

      return (uint) (this.GenerateUInt()/((double) uint.MaxValue/(maxValue - minValue)) + minValue); 
     } 


     /// <summary> 
     /// Returns a nonnegative random number. 
     /// </summary> 
     /// <returns>A 32-bit signed integer greater than or equal to zero and less than int.MaxValue.</returns> 

     public override int Next() 
     { 
      return (int) (this.GenerateUInt()/2); 
     } 


     /// <summary> 
     /// Returns a nonnegative random number less than the specified maximum. 
     /// </summary> 
     /// <param name="maxValue">The upper bound of the random number to be generated. maxValue must be greater than or equal to zero.</param> 
     /// <returns>A 32-bit signed integer greater than or equal to zero, and less than maxValue; 
     /// that is, the range of return values includes zero but not MaxValue.</returns> 

     public override int Next(int maxValue) /* throws ArgumentOutOfRangeException */ 
     { 
      if (maxValue <= 0) 
      { 
       if (maxValue == 0) 
        return 0; 
       else 
        throw new ArgumentOutOfRangeException("maxValue", "Next() called with a negative parameter"); 
      } 

      return (int) (this.GenerateUInt()/(uint.MaxValue/maxValue)); 
     } 


     /// <summary> 
     /// Returns a signed random number from a specified range. 
     /// </summary> 
     /// <param name="minValue">The lower bound of the random number returned.</param> 
     /// <param name="maxValue">The upper bound of the random number returned. maxValue must be greater than or equal to minValue.</param> 
     /// <returns>A 32-bit signed integer greater than or equal to minValue and less than maxValue; 
     /// that is, the range of return values includes minValue but not MaxValue. 
     /// If minValue equals maxValue, minValue is returned.</returns> 

     public override int Next(int minValue, int maxValue) /* ArgumentOutOfRangeException */ 
     { 
      if (minValue >= maxValue) 
      { 
       if (minValue == maxValue) 
       { 
        return minValue; 
       } 
       else 
       { 
        throw new ArgumentOutOfRangeException("minValue", "Next() called with minValue > maxValue"); 
       } 
      } 

      return (int) (this.GenerateUInt()/((double) uint.MaxValue/(maxValue - minValue)) + minValue); 
     } 


     /// <summary> 
     /// Fills an array of bytes with random numbers from 0..255 
     /// </summary> 
     /// <param name="buffer">The array to be filled with random numbers.</param> 

     public override void NextBytes(byte[] buffer) /* throws ArgumentNullException*/ 
     { 
      int bufLen = buffer.Length; 

      if (buffer == null) 
       throw new ArgumentNullException("buffer"); 

      for (int idx = 0; idx < bufLen; idx++) 
       buffer[idx] = (byte) (this.GenerateUInt()/(uint.MaxValue/byte.MaxValue)); 
     } 


     /// <summary> 
     /// Returns a double-precision random number in the range [0..1[ 
     /// </summary> 
     /// <returns>A random double-precision floating point number greater than or equal to 0.0, and less than 1.0.</returns> 

     public override double NextDouble() 
     { 
      return (double) this.GenerateUInt()/uint.MaxValue; 
     } 
    } 
} 
+0

Misterenne Twister mang lại cho tôi nhiều giá trị tốt hơn, cảm ơn bạn. Tôi vẫn đang lạm dụng các đối tượng, nhưng tôi có thể sửa đổi mã này để chỉ yêu cầu một đối tượng duy nhất. Cảm ơn đã giúp đỡ. –

+0

Giải pháp này hiện đang làm việc cho tôi, vì vậy tôi sẽ để nó như được chấp nhận, nhưng nếu bất cứ ai thấy điều này không phù hợp với vấn đề tương tự, tôi đã thêm câu trả lời từ một người bạn của tôi đề xuất sử dụng băm chức năng, có thể phù hợp với nhu cầu của bạn tốt hơn. –

0

Một cách có thể dường như để lưu trữ random.Next() giá trị cho một lần chạy thử, và đồ chúng để mỗi đầu vào. Có các giá trị này trên một kho dữ liệu, lưu chúng vào lần chạy ứng dụng tiếp theo và bắt đầu phục vụ chúng. Trong thực tế, bạn sẽ nhận được cùng một đầu ra ngẫu nhiên cho một đầu vào.

+0

Thật không may, điều này sẽ không hoạt động, vì tôi cần các giá trị giống nhau trên bất kỳ trường hợp nào của chương trình, vì nó là một công cụ thiết kế cộng tác và mọi người cần xem cùng một thứ. –

1

Nếu bạn đang cố gắng để làm cho sản lượng tái sản xuất thì bạn chỉ cần hạt giống Randommột lần với một hạt cố định.

Bạn có thể đặt hạt giống này thành đầu vào khác trong chương trình của mình. Bằng cách đó bạn sẽ biết rằng dãy số được trả về bởi Next sẽ giống hệt nhau trong hai lần thực hiện chương trình của bạn (sử dụng cùng một hạt giống).

Bạn chắc chắn nên không reinitialize trình tạo ngẫu nhiên mỗi lần.

Random rnd1 = new Random(12); 
    Random rnd2 = new Random(12); 

Hai máy phát này sẽ luôn xuất kết quả tương tự khi Next được gọi. Không quan trọng trong đó trong mã mà chúng được khai báo. Hoặc khi nào. Điều duy nhất quan trọng là seed (ở đây 12) là như nhau.

Nếu bạn muốn tập hợp các giá trị có thể tái sản xuất khác, giống hệt với giá trị kết quả từ rnd1, tất cả những gì bạn cần làm là khởi tạo rnd2.

+1

Tôi đã có một hạt giống cơ sở. Tôi cũng cần tạo ra vài nghìn số ngẫu nhiên có thể tái sản xuất, ngay cả trong một lần chạy mã. Khởi tạo ngẫu nhiên một lần với một hạt giống đã đặt sẽ ngăn không cho tôi có giá trị tái sản xuất trong cùng một lần thực hiện. –

+1

@MikePrecup Bạn * đang làm sai và cặp song sinh Mersenne sẽ không giúp bạn – Andrei

+0

Sau đó, hãy cho tôi biết cách tôi làm sai. Mersenne Twister hoạt động xuất sắc, mặc dù tôi sẽ cần phải cắt mã xuống để chỉ lấy một giá trị đầu tiên từ một hạt giống. –

0

Nếu bạn muốn 'cùng một hạt giống -> cùng một số'. nhìn này

Điều này rất đơn giản.

class MyRandom 
{ 
    private static Random Rand = new Random(); 
    private static Dictionary<int, int> LookupTable = new Dictionary<int, int>(); 

    public static int RandomInt(int seed) 
    { 
     try 
     { 
      return LookupTable[ seed ]; 
     } 
     catch (Exception e) 
     { 
      int retNum = Rand.Next(); 
      LookupTable.Add(seed, retNum); 
      return retNum; 
     } 
    } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     Console.WriteLine(MyRandom.RandomInt(3)); 
     Console.WriteLine(MyRandom.RandomInt(1)); 
     Console.WriteLine(MyRandom.RandomInt(3)); 
    } 
} 
+0

Điều gì sẽ xảy ra nếu được gọi theo thứ tự khác, ví dụ: 1, 3, 3? 1 sẽ không còn giống nhau. Điều này sẽ thay đổi giữa các lần thực hiện. –

5

Tôi ghét trả lời câu hỏi của riêng mình, nhưng một người bạn của tôi đã đưa ra đề xuất này khỏi StackOverflow và tôi cảm thấy tốt nhất nên đưa nó vào đây để có hậu thế.

Điều được hỏi thực sự chỉ là hàm băm. Nếu bạn chạy đầu vào thông qua một thuật toán băm mạnh thích hợp và chuyển đổi đầu ra thành một int, các giá trị đầu ra ngẫu nhiên tương ứng với đầu vào của chúng sẽ được tạo ra.

+1

Trả lời câu hỏi của riêng bạn trên StackOverflow là hoàn toàn OK. Bạn thậm chí có thể chấp nhận câu trả lời của riêng bạn (nhưng bạn sẽ không nhận được điểm danh tiếng cho nó). –

0

Perlin Noise hoặc mới hơn Simplex Noise hoạt động tốt để tạo cảnh quan.

Nếu tôi hiểu thuật toán một cách chính xác, nó hoạt động bằng cách thêm các mức độ nhiễu cùng nhau (nội suy tuyến tính giữa các điểm ngẫu nhiên) với các tần số khác nhau. Tôi cũng tìm thấy thêm detailed explanation.

Tôi tìm thấy một thư viện Simplex Noise trên Google Code,

Và việc thực hiện:

// SimplexNoise for C# 
// Author: Heikki Törmälä 

//This is free and unencumbered software released into the public domain. 

//Anyone is free to copy, modify, publish, use, compile, sell, or 
//distribute this software, either in source code form or as a compiled 
//binary, for any purpose, commercial or non-commercial, and by any 
//means. 

//In jurisdictions that recognize copyright laws, the author or authors 
//of this software dedicate any and all copyright interest in the 
//software to the public domain. We make this dedication for the benefit 
//of the public at large and to the detriment of our heirs and 
//successors. We intend this dedication to be an overt act of 
//relinquishment in perpetuity of all present and future rights to this 
//software under copyright law. 

//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
//EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 
//MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 
//IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 
//OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 
//ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 
//OTHER DEALINGS IN THE SOFTWARE. 

//For more information, please refer to <http://unlicense.org/> 


namespace SimplexNoise 
{ 
    /// <summary> 
    /// Implementation of the Perlin simplex noise, an improved Perlin noise algorithm. 
    /// Based loosely on SimplexNoise1234 by Stefan Gustavson <http://staffwww.itn.liu.se/~stegu/aqsis/aqsis-newnoise/> 
    /// 
    /// </summary> 
    public class Noise 
    { 
     /// <summary> 
     /// 1D simplex noise 
     /// </summary> 
     /// <param name="x"></param> 
     /// <returns></returns> 
     public static float Generate(float x) 
     { 
      int i0 = FastFloor(x); 
      int i1 = i0 + 1; 
      float x0 = x - i0; 
      float x1 = x0 - 1.0f; 

      float n0, n1; 

      float t0 = 1.0f - x0*x0; 
      t0 *= t0; 
      n0 = t0 * t0 * grad(perm[i0 & 0xff], x0); 

      float t1 = 1.0f - x1*x1; 
      t1 *= t1; 
      n1 = t1 * t1 * grad(perm[i1 & 0xff], x1); 
      // The maximum value of this noise is 8*(3/4)^4 = 2.53125 
      // A factor of 0.395 scales to fit exactly within [-1,1] 
      return 0.395f * (n0 + n1); 
     } 

     /// <summary> 
     /// 2D simplex noise 
     /// </summary> 
     /// <param name="x"></param> 
     /// <param name="y"></param> 
     /// <returns></returns> 
     public static float Generate(float x, float y) 
     { 
      const float F2 = 0.366025403f; // F2 = 0.5*(sqrt(3.0)-1.0) 
      const float G2 = 0.211324865f; // G2 = (3.0-Math.sqrt(3.0))/6.0 

      float n0, n1, n2; // Noise contributions from the three corners 

      // Skew the input space to determine which simplex cell we're in 
      float s = (x+y)*F2; // Hairy factor for 2D 
      float xs = x + s; 
      float ys = y + s; 
      int i = FastFloor(xs); 
      int j = FastFloor(ys); 

      float t = (float)(i+j)*G2; 
      float X0 = i-t; // Unskew the cell origin back to (x,y) space 
      float Y0 = j-t; 
      float x0 = x-X0; // The x,y distances from the cell origin 
      float y0 = y-Y0; 

      // For the 2D case, the simplex shape is an equilateral triangle. 
      // Determine which simplex we are in. 
      int i1, j1; // Offsets for second (middle) corner of simplex in (i,j) coords 
      if(x0>y0) {i1=1; j1=0;} // lower triangle, XY order: (0,0)->(1,0)->(1,1) 
      else {i1=0; j1=1;}  // upper triangle, YX order: (0,0)->(0,1)->(1,1) 

      // A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and 
      // a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where 
      // c = (3-sqrt(3))/6 

      float x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords 
      float y1 = y0 - j1 + G2; 
      float x2 = x0 - 1.0f + 2.0f * G2; // Offsets for last corner in (x,y) unskewed coords 
      float y2 = y0 - 1.0f + 2.0f * G2; 

      // Wrap the integer indices at 256, to avoid indexing perm[] out of bounds 
      int ii = i % 256; 
      int jj = j % 256; 

      // Calculate the contribution from the three corners 
      float t0 = 0.5f - x0*x0-y0*y0; 
      if(t0 < 0.0f) n0 = 0.0f; 
      else { 
       t0 *= t0; 
       n0 = t0 * t0 * grad(perm[ii+perm[jj]], x0, y0); 
      } 

      float t1 = 0.5f - x1*x1-y1*y1; 
      if(t1 < 0.0f) n1 = 0.0f; 
      else { 
       t1 *= t1; 
       n1 = t1 * t1 * grad(perm[ii+i1+perm[jj+j1]], x1, y1); 
      } 

      float t2 = 0.5f - x2*x2-y2*y2; 
      if(t2 < 0.0f) n2 = 0.0f; 
      else { 
       t2 *= t2; 
       n2 = t2 * t2 * grad(perm[ii+1+perm[jj+1]], x2, y2); 
      } 

      // Add contributions from each corner to get the final noise value. 
      // The result is scaled to return values in the interval [-1,1]. 
      return 40.0f * (n0 + n1 + n2); // TODO: The scale factor is preliminary! 
     } 


     public static float Generate(float x, float y, float z) 
     { 
      // Simple skewing factors for the 3D case 
      const float F3 = 0.333333333f; 
      const float G3 = 0.166666667f; 

      float n0, n1, n2, n3; // Noise contributions from the four corners 

      // Skew the input space to determine which simplex cell we're in 
      float s = (x+y+z)*F3; // Very nice and simple skew factor for 3D 
      float xs = x+s; 
      float ys = y+s; 
      float zs = z+s; 
      int i = FastFloor(xs); 
      int j = FastFloor(ys); 
      int k = FastFloor(zs); 

      float t = (float)(i+j+k)*G3; 
      float X0 = i-t; // Unskew the cell origin back to (x,y,z) space 
      float Y0 = j-t; 
      float Z0 = k-t; 
      float x0 = x-X0; // The x,y,z distances from the cell origin 
      float y0 = y-Y0; 
      float z0 = z-Z0; 

      // For the 3D case, the simplex shape is a slightly irregular tetrahedron. 
      // Determine which simplex we are in. 
      int i1, j1, k1; // Offsets for second corner of simplex in (i,j,k) coords 
      int i2, j2, k2; // Offsets for third corner of simplex in (i,j,k) coords 

      /* This code would benefit from a backport from the GLSL version! */ 
      if(x0>=y0) { 
       if(y0>=z0) 
       { i1=1; j1=0; k1=0; i2=1; j2=1; k2=0; } // X Y Z order 
       else if(x0>=z0) { i1=1; j1=0; k1=0; i2=1; j2=0; k2=1; } // X Z Y order 
       else { i1=0; j1=0; k1=1; i2=1; j2=0; k2=1; } // Z X Y order 
       } 
      else { // x0<y0 
       if(y0<z0) { i1=0; j1=0; k1=1; i2=0; j2=1; k2=1; } // Z Y X order 
       else if(x0<z0) { i1=0; j1=1; k1=0; i2=0; j2=1; k2=1; } // Y Z X order 
       else { i1=0; j1=1; k1=0; i2=1; j2=1; k2=0; } // Y X Z order 
      } 

      // A step of (1,0,0) in (i,j,k) means a step of (1-c,-c,-c) in (x,y,z), 
      // a step of (0,1,0) in (i,j,k) means a step of (-c,1-c,-c) in (x,y,z), and 
      // a step of (0,0,1) in (i,j,k) means a step of (-c,-c,1-c) in (x,y,z), where 
      // c = 1/6. 

      float x1 = x0 - i1 + G3; // Offsets for second corner in (x,y,z) coords 
      float y1 = y0 - j1 + G3; 
      float z1 = z0 - k1 + G3; 
      float x2 = x0 - i2 + 2.0f*G3; // Offsets for third corner in (x,y,z) coords 
      float y2 = y0 - j2 + 2.0f*G3; 
      float z2 = z0 - k2 + 2.0f*G3; 
      float x3 = x0 - 1.0f + 3.0f*G3; // Offsets for last corner in (x,y,z) coords 
      float y3 = y0 - 1.0f + 3.0f*G3; 
      float z3 = z0 - 1.0f + 3.0f*G3; 

      // Wrap the integer indices at 256, to avoid indexing perm[] out of bounds 
      int ii = i % 256; 
      int jj = j % 256; 
      int kk = k % 256; 

      // Calculate the contribution from the four corners 
      float t0 = 0.6f - x0*x0 - y0*y0 - z0*z0; 
      if(t0 < 0.0f) n0 = 0.0f; 
      else { 
       t0 *= t0; 
       n0 = t0 * t0 * grad(perm[ii+perm[jj+perm[kk]]], x0, y0, z0); 
      } 

      float t1 = 0.6f - x1*x1 - y1*y1 - z1*z1; 
      if(t1 < 0.0f) n1 = 0.0f; 
      else { 
       t1 *= t1; 
       n1 = t1 * t1 * grad(perm[ii+i1+perm[jj+j1+perm[kk+k1]]], x1, y1, z1); 
      } 

      float t2 = 0.6f - x2*x2 - y2*y2 - z2*z2; 
      if(t2 < 0.0f) n2 = 0.0f; 
      else { 
       t2 *= t2; 
       n2 = t2 * t2 * grad(perm[ii+i2+perm[jj+j2+perm[kk+k2]]], x2, y2, z2); 
      } 

      float t3 = 0.6f - x3*x3 - y3*y3 - z3*z3; 
      if(t3<0.0f) n3 = 0.0f; 
      else { 
       t3 *= t3; 
       n3 = t3 * t3 * grad(perm[ii+1+perm[jj+1+perm[kk+1]]], x3, y3, z3); 
      } 

      // Add contributions from each corner to get the final noise value. 
      // The result is scaled to stay just inside [-1,1] 
      return 32.0f * (n0 + n1 + n2 + n3); // TODO: The scale factor is preliminary! 
     } 

     private static byte[] perm = new byte[512] { 151,160,137,91,90,15, 
       131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23, 
       190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33, 
       88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166, 
       77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244, 
       102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196, 
       135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123, 
       5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42, 
       223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9, 
       129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228, 
       251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107, 
       49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254, 
       138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180, 
       151,160,137,91,90,15, 
       131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23, 
       190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33, 
       88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166, 
       77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244, 
       102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196, 
       135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123, 
       5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42, 
       223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9, 
       129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228, 
       251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107, 
       49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254, 
       138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180 
      }; 

     private static int FastFloor(float x) 
     { 
      return (x > 0) ? ((int)x) : (((int)x) - 1); 
     } 

     private static float grad(int hash, float x) 
     { 
      int h = hash & 15; 
      float grad = 1.0f + (h & 7); // Gradient value 1.0, 2.0, ..., 8.0 
      if ((h & 8) != 0) grad = -grad;   // Set a random sign for the gradient 
      return (grad * x);   // Multiply the gradient with the distance 
     } 

     private static float grad(int hash, float x, float y) 
     { 
      int h = hash & 7;  // Convert low 3 bits of hash code 
      float u = h<4 ? x : y; // into 8 simple gradient directions, 
      float v = h<4 ? y : x; // and compute the dot product with (x,y). 
      return ((h&1) != 0 ? -u : u) + ((h&2) != 0 ? -2.0f*v : 2.0f*v); 
     } 

     private static float grad(int hash, float x, float y , float z) { 
      int h = hash & 15;  // Convert low 4 bits of hash code into 12 simple 
      float u = h<8 ? x : y; // gradient directions, and compute dot product. 
      float v = h<4 ? y : h==12||h==14 ? x : z; // Fix repeats at h = 12 to 15 
      return ((h&1) != 0 ? -u : u) + ((h&2) != 0 ? -v : v); 
     } 

     private static float grad(int hash, float x, float y, float z, float t) { 
      int h = hash & 31;  // Convert low 5 bits of hash code into 32 simple 
      float u = h<24 ? x : y; // gradient directions, and compute dot product. 
      float v = h<16 ? y : z; 
      float w = h<8 ? z : t; 
      return ((h&1) != 0 ? -u : u) + ((h&2) != 0 ? -v : v) + ((h&4) != 0 ? -w : w); 
     } 
    } 
} 
+0

Mặc dù nó hoạt động tốt cho việc tạo cảnh quan và tôi đang sử dụng nó trong một phần riêng biệt của thuật toán, điều này không thực sự liên quan đến câu hỏi. –

Các vấn đề liên quan