Search code examples
c#rangeradix

Combinations.... Cartesian Product?


I want to generate folders using combinations of given characters i.e. 8 character text with combination of characters: abcdefghijklmnopqrstuvwxyz1234567890. For the 8 characters combinations there are 2821109907456 possibilities. I want to group these by range of 10,000.

I need to put these folders in relevant range folders i.e. 'aaaaaaa1 - aaaaaaa9' is a range of 9 combinations and a folder 'aaaaaaa3' will be created in this range folder.

I want to use c# code, give my method a folder name i.e. 'aaaaaaa3' and be returned the relevant folder range i.e. 'aaaaaa1 - aaaaaaa9' where it should be saved.

Question: I need c# code to do this!


Solution

  • From the beginning, it looks like we're going to need to compute ranges of alphanumeric sequences, which means converting them to numbers and back. An all-purpose base converter seems like the first logical step:

    /// <summary>
    /// Provides conversion between long integers and custom number bases.
    /// </summary>
    public class BaseConverter
    {
        private string _characterSet;
    
        /// <summary>
        /// Creates a new BaseConverter.
        /// </summary>
        /// <param name="characterSet">The characters in the custom base, in  
        /// increasing order of value.</param>
        public BaseConverter(string characterSet = 
           "0123456789abcdefghijklmnopqrstuvwxyz")
        {
            _characterSet = characterSet;
        }
    
        /// <summary>
        /// Converts a number in the custom base system to a long.
        /// </summary>
        /// <param name="value">The custom base number to convert.</param>
        /// <returns>The long form of the custom base number.</returns>
        public long StringToLong(string value)
        {
            if (value == Convert.ToString(_characterSet[0])) return 0;
            long val = 0; 
            string text = value[0] == '-' ? value.Substring(1, 
               value.Length - 1) : value;
    
            for (int i = text.Length, power = 0; i != 0; i--, power++)
            {
                val += (long)Math.Round((_characterSet.IndexOf(text[i-1]) * 
                   Math.Pow(_characterSet.Length, power)));
            }
    
            return value[0] == '-' ? -val : val;
        }
    
        /// <summary>
        /// Converts a long to the custom base system.
        /// </summary>
        /// <param name="value">The long to convert.</param>
        /// <returns>The custome base number version of the long.</returns>
        public string LongToString(long value)
        {
            if (value == 0) return Convert.ToString(_characterSet[0]);
            long number = value.Abs();
            int remainder;
            StringBuilder text = new StringBuilder((int)Math.Round(
               Math.Log(long.MaxValue, (double)_characterSet.Length)) + 
               value < 0 ? 1 : 0);
    
            while (number != 0)
            {
                remainder = (int)(number % _characterSet.Length);
                text.Insert(0, _characterSet[remainder]);
                number -= remainder;
                number /= _characterSet.Length;
            }
    
            if (value < 0) text.Insert(0, "-");
            return text.ToString();
        }
    

    Then, you'll need the code to compute your ranges:

    ///<summary>
    ///Computes numeric ranges using a BaseConverter.
    ///</summary>
    public class NumericRangeFactory
    {
       private long _min, _length;
       private BaseConverter _converter;
    
       //creates a new NumericRangeFactory
       //converter - the BaseConverter that defines the number system 
       //being used
       //min - the smallest value in an acceptable range
       //length - the number of values in a single range
       public NumericRangeFactory(BaseConverter converter, long min, 
          long length)
       {
          _converter = converter; _min = min; _length = length;
       }
    
       public NumericRangeFactory(BaseConverter converter, string min, 
          long length) : this(converter.StringToLong(min), length) {}
    
       //returns an array of long containing the min and max of the 
       //range that contains value
       public long[] GetLongRange(long value)
       {
          long min = _length * (value / _length); //todo: fix non-zero _min case
          return new long[] { min, min + length - 1 };    
       }
    
       public long[] GetLongRange(string value)
       {
          return GetLongRange(_converter.StringToLong(value));
       }
    
       //returns an array of string containing the min and max of 
       //the range that contains value
       public string[] GetStringRange(long value)
       {
          long[] range = GetLongRange(value);
          return new string[] {_converter.LongToString(range[0]),
             _converter.LongToString(range[1])};
       }
    
       public string[] GetStringRange(string value)
       {
          return GetStringRange(_converter.StringToLong(value));
       }
    }
    

    Finally, tie the BaseConverter and NumericRangeFactory classes together to solve the problem with this sample static method:

    public static string GetFolderRange(string folderName)
    {
       BaseConverter converter = new BaseConverter();
       NumericRangeFactory rangeFactory = new NumericRangeFactory(converter, 
          "aaaaaaa0", 9);
       string[] range = rangeFactory.GetStringRange(folderName);
       return range[0] + "-" + range[1];
    }
    

    I haven't tested this, but I think the concept is solid.