code: def split(
      self,
      df: DataFrame,
      backend: Backend,
      random_seed: float = default_random_seed,
  ) -> Tuple[DataFrame, DataFrame, DataFrame]:
      probabilities = self.probabilities
      if not backend.df_engine.partitioned:
          divisions = _split_divisions_with_min_rows(
              len(df), probabilities
          )
          shuffled_df = df.sample(frac=1, random_state=random_seed)
          return (
              shuffled_df.iloc[: divisions[0]],  # Train
              shuffled_df.iloc[
                  divisions[0] : divisions[1]
              ],  # Validation
              shuffled_df.iloc[divisions[1] :],  # Test
          )
  
      # The above approach is very inefficient for partitioned backends, which can split by partition.
      # This does not give exact guarantees on split size but is much more efficient for large datasets.
      return df.random_split(
          self.probabilities, random_state=random_seed
      )
  
imports: import logging
  from abc import ABC, abstractmethod
  from typing import List, Optional, Tuple, TYPE_CHECKING
  from zlib import crc32
  import numpy
  from sklearn.model_selection import train_test_split
  from ludwig.api_annotations import DeveloperAPI
  from ludwig.backend.base import Backend
  from ludwig.constants import BINARY, CATEGORY, DATE, MIN_DATASET_SPLIT_ROWS, SPLIT
  from ludwig.error import ConfigValidationError
  from ludwig.schema.split import DateTimeSplitConfig, FixedSplitConfig, HashSplitConfig, RandomSplitConfig, StratifySplitConfig
  from ludwig.types import ModelConfigDict, PreprocessingConfigDict
  from ludwig.utils.data_utils import hash_dict, split_dataset_ttv
  from ludwig.utils.defaults import default_random_seed
  from ludwig.utils.registry import Registry
  from ludwig.utils.types import DataFrame
label_desc: Use image node_id 2 for calling the RandomSplitter obj's underlying member method code with example usage: obj.split(df, backend, random_seed) and returns: df, shuffled_df,  shuffled_df,  shuffled_df