【问题标题】:Generate an array of numbers without duplicate in Solidity在 Solidity 中生成不重复的数字数组
【发布时间】:2022-01-13 20:44:02
【问题描述】:

我正在构建一个彩票智能合约,用户可以在其中购买一张彩票,该彩票具有从 1 到 25 的 5 个生成数字,而没有重复的数字。

一切都很好,除了 30% 的时间,Metamask 说“交易用完了气体”,因为这个功能。

下面的函数生成一个由 1 到 25 之间的 5 个数字组成的数组:

  /// @dev Generate 5 numbers ( 1 <= x >= 25) without duplicate numbers
  /// @param _lotteryCount used for the random generator number function
  /// @return uint256[5] return an array of 5 generated uint

  function generateRandomTicketNumbers(uint256 _lotteryCount) internal view returns (uint8[5] memory) {
    uint8[5] memory numbers;
    uint256 generatedNumber;

    // Execute 5 times (to generate 5 numbers)
    for (uint256 i = 0; i < 5; i++) {
      //   Check duplicate
      bool readyToAdd = false;
      uint256 maxRetry = 5;
      uint256 retry = 0;

      // Generate a new number while it is a duplicate, up to 5 times (to prevent errors and infinite loops)
      while (!readyToAdd && retry <= maxRetry) {
        generatedNumber = (uint256(keccak256(abi.encodePacked(msg.sender, block.timestamp, i, retry, _lotteryCount))) % 25).add(1);
        bool isDuplicate = false;

        // Look in all already generated numbers array if the new generated number is already there.
        for (uint256 j = 0; j < numbers.length; j++) {
          if (numbers[j] == generatedNumber) {
            isDuplicate = true;
            break;
          }
        }
        readyToAdd = !isDuplicate;
        retry++;
      }
      // Throw if we hit maximum retry : generated a duplicate 5 times in a row.
      require(retry < maxRetry, 'Error generating random ticket numbers. Max retry.');
      numbers[i] = uint8(generatedNumber);
    }

    return numbers;
  }

我不确定 Metamask 如何估算交易的 gas,但我猜它在本地运行交易,查看它使用了多少 gas,然后将这个数量用于实际交易。

如果这是正确的,那就可以解释为什么 30% 的时间都失败了。有时,该函数需要多次重试才能在“while”循环中生成一个数字,而有时,“while”循环只执行一次。

我可以强制函数在每个“for”和“while”循环中运行最大次数,但我相信有比浪费气体更好的解决方案。

你知道我该如何解决这个问题吗?

谢谢!

【问题讨论】:

  • Fisher-Yates shuffle 只需 5 次调用随机数生成器即可完成任务。
  • 谢谢,这正是我想要的!

标签: algorithm ethereum solidity smartcontracts


【解决方案1】:

首先使用 chainlink 作为随机源 https://docs.chain.link/docs/chainlink-vrf/ ,遵循最佳实践部分 https://docs.chain.link/docs/chainlink-vrf-best-practices/ ,然后当您获得随机数时,您可以按照文档中的建议进行操作

function expand(
    uint256 randomValue, 
    uint256 n
) public pure returns (uint256[] memory expandedValues) {
    expandedValues = new uint256[](n);
    for (uint256 i = 0; i < n; i++) {
        expandedValues[i] = uint256(keccak256(abi.encode(randomValue, i)));
    }
    return expandedValues;
}

【讨论】:

    猜你喜欢
    • 2019-08-11
    • 2022-11-10
    • 2016-05-05
    • 2014-04-25
    • 2014-11-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多