【问题标题】:Random number between int.MinValue and int.MaxValue, inclusiveint.MinValue 和 int.MaxValue 之间的随机数,包括
【发布时间】:2019-11-28 19:09:31
【问题描述】:

这里有点令人困惑:Random.Next() 有一个接受最小值和最大值的重载。此重载返回一个大于或等于最小值(含)且小于最大值(不含)的数字。

我想包括整个范围,包括最大值。在某些情况下,我只需将最大值加一即可完成此操作。但是在这种情况下,最大值可以是int.MaxValue,在上面加一并不能达到我想要的效果。

那么有谁知道一个从int.MinValueint.MaxValue 的随机数的好技巧吗?

更新:

请注意,下限可以是int.MinValue,但也可以是其他值。如果我知道它总是int.MinValue 那么问题会更简单。

【问题讨论】:

  • 将重载与int.MinValueint.MaxValue 一起使用;如果它曾经产生int.MaxValue - 1,随机添加 1 :)。
  • @HereticMonkey:但是它永远不会产生int.MaxValue - 1
  • 是的,它可以。在滚动int.MaxValue - 1 时,您将在int.MaxValueint.MaxValue - 1 之间随机选择
  • @HereticMonkey 没有说“总是”。他们说“随机”。
  • 确实,这有点儿笑话。有人可能会称其为一个随机的笑话......

标签: c# .net random


【解决方案1】:

Random.Next(int minValue, int maxValue)internal implementation 为大范围生成两个样本,例如Int32.MinValueInt32.MaxValue 之间的范围。对于NextInclusive 方法,我不得不使用另一个大范围Next,总共四个样本。所以性能应该与用 4 个字节(每个字节一个样本)填充缓冲区的版本相当。

public static class RandomExtensions
{
    public static int NextInclusive(this Random random, int minValue, int maxValue)
    {
        if (maxValue == Int32.MaxValue)
        {
            if (minValue == Int32.MinValue)
            {
                var value1 = random.Next(Int32.MinValue, Int32.MaxValue);
                var value2 = random.Next(Int32.MinValue, Int32.MaxValue);
                return value1 < value2 ? value1 : value1 + 1;
            }
            return random.Next(minValue - 1, Int32.MaxValue) + 1;
        }
        return random.Next(minValue, maxValue + 1);
    }

}

一些结果:

new Random(0).NextInclusive(int.MaxValue - 1, int.MaxValue); // returns int.MaxValue
new Random(1).NextInclusive(int.MaxValue - 1, int.MaxValue); // returns int.MaxValue - 1
new Random(0).NextInclusive(int.MinValue, int.MinValue + 1); // returns int.MinValue + 1
new Random(1).NextInclusive(int.MinValue, int.MinValue + 1); // returns int.MinValue
new Random(24917099).NextInclusive(int.MinValue, int.MaxValue); // returns int.MinValue
var random = new Random(784288084);
random.NextInclusive(int.MinValue, int.MaxValue);
random.NextInclusive(int.MinValue, int.MaxValue); // returns int.MaxValue

更新:我的实现在尽可能大的范围内(Int32.MinValue - Int32.MaxValue)性能一般,所以我想出了一个速度快 4 倍的新实现。它在我的机器中每秒产生大约 22,000,000 个随机数。我认为它不会比这更快。

public static int NextInclusive(this Random random, int minValue, int maxValue)
{
    if (maxValue == Int32.MaxValue)
    {
        if (minValue == Int32.MinValue)
        {
            var value1 = random.Next() % 0x10000;
            var value2 = random.Next() % 0x10000;
            return (value1 << 16) | value2;
        }
        return random.Next(minValue - 1, Int32.MaxValue) + 1;
    }
    return random.Next(minValue, maxValue + 1);
}

一些结果:

new Random(0).NextInclusive(int.MaxValue - 1, int.MaxValue); // = int.MaxValue
new Random(1).NextInclusive(int.MaxValue - 1, int.MaxValue); // = int.MaxValue - 1
new Random(0).NextInclusive(int.MinValue, int.MinValue + 1); // = int.MinValue + 1
new Random(1).NextInclusive(int.MinValue, int.MinValue + 1); // = int.MinValue
new Random(1655705829).NextInclusive(int.MinValue, int.MaxValue); // = int.MaxValue
var random = new Random(1704364573);
random.NextInclusive(int.MinValue, int.MaxValue);
random.NextInclusive(int.MinValue, int.MaxValue);
random.NextInclusive(int.MinValue, int.MaxValue); // = int.MinValue

【讨论】:

  • 您是否考虑到 Random.Next() 只返回正数?
  • @JonathanWood 我没有使用不带参数的Random.Next() 重载。我使用了Random.Next(int minValue, int maxValue)。我本可以使用Next() 将样本减少到三个而不是四个,但这会使数学复杂化,我希望保持简单。
  • 我想,这个问题得到了足够的关注。目前我们有 3 个得分最高的答案。一个是我的。另一个是相当不完整的。所以这个奖是给这个的。感谢大家。谢谢你,西奥多·祖利亚斯。
【解决方案2】:

没有强制转换,没有long,考虑了所有边界情况,最佳性能。

static class RandomExtension
{
    private static readonly byte[] bytes = new byte[sizeof(int)];

    public static int InclusiveNext(this Random random, int min, int max)
    {
        if (max < int.MaxValue)
            // can safely increase 'max'
            return random.Next(min, max + 1);

        // now 'max' is definitely 'int.MaxValue'
        if (min > int.MinValue)
            // can safely decrease 'min'
            // so get ['min' - 1, 'max' - 1]
            // and move it to ['min', 'max']
            return random.Next(min - 1, max) + 1;

        // now 'max' is definitely 'int.MaxValue'
        // and 'min' is definitely 'int.MinValue'
        // so the only option is
        random.NextBytes(bytes);
        return BitConverter.ToInt32(bytes, 0);
    }
}

【讨论】:

  • 看起来没有理想的解决方案,但这看起来还不错。
  • @Jonathan Wood,我想是的。但我期待新的好主意。这就是赏金的目的)
  • 如果minmax 相等,那真的会引发异常吗?如果您对此改变主意(或者实际上并非有意),那么您可以安全地摆脱第一次检查(和异常),因为Random.Next(int, int) 会为您处理(无论何时max &lt; min)。
  • “它意外返回int.MaxValue - 1 嗯,看起来它应该返回int.MaxValue 就好了(因为你的random.Next(min - 1, max) + 1)。为了清楚起见,我并不是说在min == max 时抛出异常的决定必然(总是)是一个糟糕的决定。但是,使用该库的开发人员可能出于多种原因并不期望它(其中一个原因是它不是现有方法的行为(即Random.Next(x, x))。尽管人们会可能有(有效?)两个方向的论据。
  • @Ahmed Abdelhameed,我想,你是对的。检查是多余的和误导性的。感谢您注意到这一点。
【解决方案3】:

好吧,我有一个窍门。我不确定我是否会将其描述为“好把戏”,但我觉得它可能会奏效。

public static class RandomExtensions
{
    public static int NextInclusive(this Random rng, int minValue, int maxValue)
    {
        if (maxValue == int.MaxValue)
        {
            var bytes = new byte[4];
            rng.NextBytes(bytes);
            return BitConverter.ToInt32(bytes, 0);
        }
        return rng.Next(minValue, maxValue + 1);
    }
}

所以,基本上是一种扩展方法,如果上限为int.MaxValue,则将简单地生成四个字节并转换为int,否则只需使用标准的Next(int, int) 重载。

请注意,如果maxValueint.MaxValue,它将忽略minValue。我想我没有考虑到这一点......

【讨论】:

  • 这忽略了maxValue == int.MaxValue 的下限,但我想知道如果我可以进一步改进它,类似这种方法。
  • 是的,我在发帖后意识到了同样的事情:(。
  • 在此基础上扩展,你可以创建 int 变量并将其设置为 0,然后将各个位随机设置为 0 或 1 以获得随机数吗?
【解决方案4】:

将范围一分为二,并补偿MaxValue

r.Next(2) == 0 ? r.Next(int.MinValue, 0) : (1 + r.Next(-1, int.MaxValue))

如果我们使范围大小相等,我们可以用不同的数学得到相同的结果。这里我们依赖int.MinValue = -1 - int.MaxValue:

r.Next(int.MinValue, 0) - (r.Next(2) == 0 ? 0 : int.MinValue)

【讨论】:

  • 我喜欢这个答案,它非常简单有效。
  • 或者它可以在这两种情况下使用较低的全半范围 [int.MinValue, 0) 并反转正数的符号。这可以简化一点逻辑,并节省您稍后必须做出的解释:)
  • @tgralex 无法反转符号。绝对(int.MinLength)!=绝对(int.MaxLength)。按绝对值计算,它实际上更大。
  • int.MaxValue: 2,147,483,647 int.MinValue: -2,147,483,648 这意味着 Abs(int.MinValue) = Abs(int.MaxValue)+1 所以我们正在寻找的范围是 (-Abs(int. MinValue), +Abs(int.MinValue)) ,因此我建议简化为: var sign = (r.Next(2) == 0 ? 1 : 1);返回符号 * r.Next(0, Abs(int.MinValue));就这些。它对我来说只是看起来稍微简单一点:)
  • @tgralex 无法将值Abs(int.MinValue) 赋予Next,因为没有这样的值。但是假设我们调用Next(int.MinValue, 0),那么你永远不会得到0,而且如果Next实际上返回MinValue,那么反转符号也会导致错误,因为-int.MinValue不存在。
【解决方案5】:

我建议像这样使用System.Numerics.BigInteger

class InclusiveRandom
{
    private readonly Random rnd = new Random();

    public byte Next(byte min, byte max) => (byte)NextHelper(min, max);
    public sbyte Next(sbyte min, sbyte max) => (sbyte)NextHelper(min, max);
    public short Next(short min, short max) => (short)NextHelper(min, max);
    public ushort Next(ushort min, ushort max) => (ushort)NextHelper(min, max);
    public int Next(int min, int max) => (int)NextHelper(min, max);
    public uint Next(uint min, uint max) => (uint)NextHelper(min, max);
    public long Next(long min, long max) => (long)NextHelper(min, max);
    public ulong Next(ulong min, ulong max) => (ulong)NextHelper(min, max);

    private BigInteger NextHelper(BigInteger min, BigInteger max)
    {
        if (max <= min)
            throw new ArgumentException($"max {max} should be greater than min {min}");

        return min + RandomHelper(max - min);
    }

    private BigInteger RandomHelper(BigInteger bigInteger)
    {
        byte[] bytes = bigInteger.ToByteArray();
        BigInteger random;

        do
        {
            rnd.NextBytes(bytes);
            bytes[bytes.Length - 1] &= 0x7F;
            random = new BigInteger(bytes);
        } while (random > bigInteger);

        return random;
    }
}

我用sbyte测试了它。

var rnd = new InclusiveRandom();
var frequency = Enumerable.Range(sbyte.MinValue, sbyte.MaxValue - sbyte.MinValue + 1).ToDictionary(i => (sbyte)i, i => 0ul);
var count = 100000000;
for (var i = 0; i < count; i++)
    frequency[rnd.Next(sbyte.MinValue, sbyte.MaxValue)]++;
foreach (var i in frequency)
    chart1.Series[0].Points.AddXY(i.Key, (double)i.Value / count);

chart1.ChartAreas[0].AxisY.StripLines
    .Add(new StripLine { Interval = 0, IntervalOffset = 1d / 256, StripWidth = 0.0003, BackColor = Color.Red });

分布正常。

【讨论】:

  • 请问bytes[bytes.Length - 1] &amp;= 0x7F;这行的目的是什么?您似乎正在消除负数?
  • @Jonathan Wood,在这里我尝试在更大和更小的数字之间获得随机差异。此差异始终为正数。
  • 是的,我明白了。您将其添加到最小值。对这里的性能有些担心,但是当我有时间时会玩这个。
  • @Jonathan Wood,毫无疑问,这段代码会受到优化(例如,对于非边界条件,其中一个不处理最小值和最大值),但它通常适用于所有非浮点数。
【解决方案6】:

这保证适用于负值和非负值:

public static int NextIntegerInclusive(this Random r, int min_value, int max_value)
{
  if (max_value < min_value)
  {
    throw new InvalidOperationException("max_value must be greater than min_value.");
  }
  long offsetFromZero =(long)min_value; // e.g. -2,147,483,648
  long bound = (long)max_value; // e.g. 2,147,483,647
  bound -= offsetFromZero; // e.g. 4,294,967,295 (uint.MaxValue)
  bound += Math.Sign(bound); // e.g. 4,294,967,296 (uint.MaxValue + 1)
  return (int) (Math.Round(r.NextDouble() * bound) + offsetFromZero); // e.g. -2,147,483,648 => 2,147,483,647
}

【讨论】:

  • 这个分布不好。 new Random().NextIntegerInclusive(Int32.MinValue, Int32.MaxValue) 永远不会返回此范围内数字的一半。例如Int32.MinValue + 1Int32.MaxValue - 1 永远不能通过这种方法生成,前提是Randomcurrent implementation 将保持不变。
  • 我知道 System.Random 有问题,但问题不是如何修复 System.Random。我已经在上面投票了@Alex的答案,因为这是一个更好的答案。这应该是选定的答案。
  • 不要为此责备RandomRandom 类本身可以产生给定范围内的所有整数(诚然没有完全公平的分布)。您的实施存在更严重的问题!
  • 也许我不明白你的第一条评论。你能详细说明为什么这永远不会返回该范围内的一半数字吗?我看不到你在看什么。
  • 啊,@Theodor,我看到了私人GetSampleForLargeRange()。我没有意识到这一点。感谢您指出。
【解决方案7】:

有趣的是,这不是 Random.Next(int, int) 的实现,因为您可以从包含的行为中推导出独占的行为,但反之则不行。

public static class RandomExtensions
{
    private const long IntegerRange = (long)int.MaxValue - int.MinValue;

    public static int NextInclusive(this Random random, int minValue, int maxValue)
    {
        if (minValue > maxValue)
        {
            throw new ArgumentOutOfRangeException(nameof(minValue));
        }

        var buffer = new byte[4];
        random.NextBytes(buffer);
        var a = BitConverter.ToInt32(buffer, 0);
        var b = a - (long)int.MinValue;
        var c = b * (1.0 / IntegerRange);
        var d = c * ((long)maxValue - minValue + 1);
        var e = (long)d + minValue;
        return (int)e;
    }
}
new Random(0).NextInclusive(int.MaxValue - 1, int.MaxValue); // returns int.MaxValue
new Random(1).NextInclusive(int.MaxValue - 1, int.MaxValue); // returns int.MaxValue - 1
new Random(0).NextInclusive(int.MinValue, int.MinValue + 1); // returns int.MinValue + 1
new Random(1).NextInclusive(int.MinValue, int.MinValue + 1); // returns int.MinValue
new Random(-451732719).NextInclusive(int.MinValue, int.MaxValue); // returns int.MinValue
new Random(-394328071).NextInclusive(int.MinValue, int.MaxValue); // returns int.MaxValue

【讨论】:

    【解决方案8】:

    据我了解,您希望 Random 输出介于 -2.147.483.648 和 +2.147.483.647 之间的值。但问题是,给定这些值的 Random 只会给出从 -2.147.483.648 到 +2.147.483.646 的值,因为最大值是互斥的。

    选项 0:拿走东西,学会不做事

    Douglas Adams 不是程序员 AFAIK,但他对我们有一些很好的建议:“使任何东西隐形所涉及的技术非常复杂,以至于 9990 亿、99900 万、9 19.9 万,999 次中的一万亿次,把东西拿走,不用它就更简单,更有效。”

    可能是这样的。

    选项 1:我们需要更大的随机数!

    Random.Next() 使用 Int32 作为参数。我能想到的一个选择是使用不同的随机函数,它可以将下一个更高级别的整数(Int64)作为输入。 Int32 被隐式转换为 Int64。 Int64 Number = Int64(Int32.MaxValue)+1;

    但是,afaik,您必须离开 .NET 库才能执行此操作。此时,您不妨寻找一个包含 Max 的 Random。

    但我认为它必须排除一个值是有数学原因的。

    选项 2:滚动更多

    另一种方法是使用两个 Random 调用 - 每个调用范围的一半 - 然后添加它们。

    Number1 = rng.Next(-2.147.483.648, 0);
    Number2 = rng.Next(0, 2.147.483.647);
    resut = Number1 + Number2;
    

    但是,我 90% 确定这会破坏随机分布。我的 P&P RPG 经验给了我一些骰子机会的经验,而且我知道事实上掷 2 个骰子(或相同的 2 次)会得到与一个特定骰子截然不同的结果分布。如果您不需要这种随机分布,这是一种选择。但是,如果您不太关心分布,则值得一试。

    选项 3:您需要全系列吗?还是您只关心其中的最小值和最大值?

    我假设您正在进行某种形式的测试,并且您需要 Int.MaxValue 和 Int.MinValue 都在该范围内。但是您是否也需要介于两者之间 的每个值,或者您可以不使用其中一个吗? 如果你必须失去价值,你会更喜欢失去 4 而不是 Int.MaxValue?

    Number = rng.Next(Int.MinValue, Int.MaxValue);
    if(Number > 3)
      Number = Number +1;
    

    这样的代码可以得到 MinValue 和 MaxValue 之间的每个数字,除了 4。但大多数情况下,能处理 3 和 5 的代码也能处理 4。不需要显式测试 4。

    当然,假设 4 不是必须运行的某个重要的测试编​​号(出于这些原因,我避免使用 1 和 0)。您也可以随机决定“跳过”的数字:

    skipAbleNumber = rng.Next(Int.MinValue +1, Int.MaxValue);
    

    然后使用&gt; skipAbleNumber 而不是&gt; 4

    【讨论】:

    • 正如我在别处解释过的,我正在编写一个通用库。所以说我不需要这个数字是不够的。我需要考虑图书馆用户可能需要的东西。而且我已经确定包含结果范围更有意义。所以,对于大多数人来说,这只是一个心理难题,而不是每天的编程任务。
    • @JonathanWood:选项 3 的优点是用户不需要关心数字是如何生成的。并且它应该仍然遵循统计分布的基本规则。但可以肯定的是,最好问问数学专家——这远远超出了我的数学技能水平。唯一的其他选择是编写自己的随机数生成器。在这一点上,您可能会了解为什么一开始就排除了一个数字。
    • @JonathanWood:正如我所怀疑的那样,Javas Inter Random 也具有排他性限制:docs.oracle.com/javase/8/docs/api/java/util/Random.html 而 C++ Random 似乎忽略了低于 0 的任何内容:en.cppreference.com/w/cpp/numeric/random/rand 我的猜测是他们正在使用一些涉及一个无符号的 Int32,并且该值不能为 0 或此数学的最大值。
    • 用户会期望范围内的所有数字都是可能的结果。这是我关心的部分,而不是数字是如何产生的。至于为什么Random.Next()不包含最大值,它可以简化算法,但在很多情况下也很有用。例如,如果您将结果用作集合的索引。
    • @JonathanWood “用户会期望范围内的所有数字都是可能的结果。这是我关心的部分,而不是数字的生成方式。”我希望 Random.Next 的结果具有包容性。事实证明他们不是。快速检查显示,这似乎在所有高级语言中都有。解释脚本语言似乎对此更加松散。我目前的猜测是,您可以忽略数字的生成方式,因为这是原因并非每个值都可以包含在内。
    【解决方案9】:

    您不能使用Random.Next() 来实现您想要的,因为您不能将N 的数字序列对应到N+1 并且不会错过一个:)。期间。

    但是你可以使用Random.NextDouble(),它返回双精度结果: 0 &lt;= x &lt; 1又名[0, 1) 介于 0 之间,其中 [ 是包含符号,) 是排除符号

    我们如何将 N 个数字对应到 [0, 1)? 您需要将 [0, 1) 拆分为 N 个相等的段: [0, 1/N), [1/N, 2/N), ... [N-1/N, 1)

    这里很重要的是一个边界是包容的,另一个是排斥的——所有 N 段绝对相等!

    这是我的代码:我把它做成了一个简单的控制台程序。

    class Program
        {
            private static Int64 _segmentsQty;
            private static double _step;
            private static Random _random = new Random();
    
            static void Main()
            {
                InclusiveRandomPrep();
                for (int i = 1; i < 20; i++)
                {
                    Console.WriteLine(InclusiveRandom());
                }
    
                Console.ReadLine();
            }
    
            public static void InclusiveRandomPrep()
            {
                _segmentsQty = (Int64)int.MaxValue - int.MinValue;
                _step = 1.0 / _segmentsQty;
            }
            public static int InclusiveRandom()
            {
                var randomDouble = _random.NextDouble();
                var times = randomDouble / _step;
                var result = (Int64)Math.Floor(times);
                return (int)result + int.MinValue;
            }
        }
    

    【讨论】:

    • 我真的不确定你的建议。一个例子肯定有助于澄清。
    • 已更新。立即检查。
    【解决方案10】:

    此方法可以为您提供任意整数范围内的随机整数。如果最大限制小于 int.MaxValue,则它使用普通的 Random.Next(Int32, Int32) 但将上限加 1 以包含其值。如果不是,但下限大于 int.MinValue,则将下限降低 1 以将范围 1 移动到将结果加 1 的范围内。最后,如果两个限制都是 int.MinValue 和 int.MaxValue,它会生成一个随机整数“a”,它是 0 或 1,每个都有 50% 的概率,然后它会生成另外两个整数,第一个是 int.MinValue 和-1 包括 2147483648 个值,第二个介于 0 和 int.MaxValue 之间,也包括 2147483648 个值,并且将它们与 'a' 的值一起使用它选择一个具有完全相等概率的整数。

    private int RandomInclusive(int min, int max)
    {
        if (max < int.MaxValue)
            return Random.Next(min, max + 1);
        if (min > int.MinValue)
            return Random.Next(min - 1, max) + 1;
        int a = Random.Next(2);
        return Random.Next(int.MinValue, 0) * a + (Random.Next(-1, int.MaxValue) + 1) * (1 - a);
    }
    

    【讨论】:

    • 感谢您的回复,但我认为int.MinValue - 1 不会产生预期的结果。
    • 这不起作用,因为最小值会回到最大值。
    • yourmin - 1 如果是int.MinValue,则溢出。
    • 对不起,我错过了 minValue。试试这个新的。
    • @JonathanWood 这个怎么样?
    【解决方案11】:

    这个呢?

    using System;
    
    public class Example
    {
       public static void Main()
       {
          Random rnd = new Random();
          int min_value = max_value;
          int max_value = min_value;
          Console.WriteLine("\n20 random integers from 10 to 20:");
          for (int ctr = 1; ctr <= 20; ctr++) 
          {
             Console.Write("{0,6}", rnd.Next(min_value, max_value));
             if (ctr % 5 == 0) Console.WriteLine();
          }
       }
    }
    

    【讨论】:

    • 根据我的问题尝试使用min_value = int.MinValue; max_value = int.MaxValue。它不会产生正确的结果。我不确定你是否理解了这个问题。
    【解决方案12】:

    你可以试试这个。有点hacky,但可以让你同时包含最小值和最大值。

    static void Main(string[] args)
    {
        int x = 0;
        var r = new Random();
        for (var i = 0; i < 32; i++)
            x = x | (r.Next(0, 2) << i);
         Console.WriteLine(x);
         Console.ReadKey();
    }
    

    【讨论】:

    • 是的,很欣赏这个建议,但这不是很高效,除了是hacky。我认为我至少会使用Random.NextBytes()
    【解决方案13】:

    您可以将1 随机添加到生成的数字中,这样它仍然是随机的并覆盖整个整数范围。

    public static class RandomExtension
    {
        public static int NextInclusive(this Random random, int minValue, int maxValue)
        {
            var randInt = random.Next(minValue, maxValue);
            var plus = random.Next(0, 2);
            return randInt + plus;
        }
    }
    

    【讨论】:

    • 是的,它是随机的。但是概率呢?它仍然相等吗?
    • 为什么是var randInt = random.Next(int.MinValue, int.MaxValue); 而不是var randInt = random.Next(minValue, maxValue);
    • @JonathanWood 它可以返回 minValue,为什么不呢?
    • @Alex,不,概率分布不均匀。例如,要获得 5,randIntplus 可以是(5 和 0)或(4 和 1)。但要获得int.MinValue,它们只能是(int.MinValue 和 0)。而要获得int.MaxValue,它们只能是(int.MaxValue - 1 和 1)。这两个极值的概率是其他值的一半。
    【解决方案14】:

    这对你有用吗?

    int random(Random rnd, int min, int max) 
    {
        return Convert.ToInt32(rnd.NextDouble() * (max - min) + min);
    }
    

    【讨论】:

    • 我认为这不会返回最大值。从数学上讲,你有 0.0 0 min
    猜你喜欢
    • 2021-11-24
    • 2012-03-07
    • 1970-01-01
    • 2014-06-23
    • 2014-04-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-30
    相关资源
    最近更新 更多