LoginSignup
1
2

More than 5 years have passed since last update.

指定した確率で配列要素を選択する

Last updated at Posted at 2018-09-02

どのゲームでも毎回同じように重み付けの実装をしているのでメモ
ロジック自体はどの言語でも一緒だけど、今回はUE4C++でテストします

#pragma once

#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "MyBlueprintFunctionLibrary.generated.h"

USTRUCT()
struct SANDBOX_API FSomething
{
    GENERATED_USTRUCT_BODY()

    UPROPERTY()
    int32 Id;

    UPROPERTY()
    int32 Weight;

    FSomething( int32 id = 0, int32 weight = 0 ) : Id( id ), Weight( weight ) {}
};

UCLASS()
class SANDBOX_API UMyBlueprintFunctionLibrary : public UBlueprintFunctionLibrary
{
    GENERATED_BODY()

    template <class T>
    static int32 Choice( const TArray<T>& source,
        const std::function<uint32( const T* )> getWeightCallback )
    {
        check( source.Num() != 0 );

        uint64 sum = 0;

        TArray<uint32> weightArray;
        for ( const auto& row : source )
        {
            const auto weight = getWeightCallback( &row );

            sum += weight;
            weightArray.Emplace( weight );
        }

        if ( sum == 0 )
        {
            return -1;
        }

        const auto r = FMath::RandRange( 1, sum );

        uint64 tmp = 0;
        for ( int32 i = 0; i < weightArray.Num(); ++i )
        {
            tmp += weightArray[ i ];
            if ( r <= tmp )
            {
                return i;
            }
        }

        checkNoEntry();

        return -1;
    }

    UFUNCTION( BlueprintCallable )
    static int32 ChoiceTest()
    {
        TArray<FSomething> someArray =
        {
            FSomething( 1, 100 ),
            FSomething( 2, 200 ),
            FSomething( 3, 150 ),
        };

        return Choice<FSomething>( someArray, [ & ] ( const auto row )
        {
            return row->Weight;
        } );
    }
};

321c72487627fa9a6d141fcff38db195.png

実際にやってみると結構偏りがでます

LogBlueprintUserMessages: [Untitled_C_10] 0: 2229
LogBlueprintUserMessages: [Untitled_C_10] 1: 4460
LogBlueprintUserMessages: [Untitled_C_10] 2: 3311

const auto r = FMath::RandRange( 1, sum );
これはUE4関数で乱数を生成しているためで、もっと精度を高めたいのであれば他の乱数生成アルゴリズムを用いる必要があります

UE4の乱数生成BPの実装を見てみる(1)(Random integer in range)
C++11 の乱数ライブラリ

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2