





'''simple docstring'''






from __future__ import annotations

import random

# Maximum size of the population.  Bigger could be faster but is more memory expensive.
lowerCAmelCase									=   2_00
# Number of elements selected in every generation of evolution. The selection takes
# place from best to worst of that generation and must be smaller than N_POPULATION.
lowerCAmelCase									=   50
# Probability that an element of a generation can mutate, changing one of its genes.
# This will guarantee that all genes will be used during evolution.
lowerCAmelCase									=   0.4
# Just a seed to improve randomness required by the algorithm.
random.seed(random.randint(0, 10_00))






def   __A    (				a_    :	str   ,a_    :	str						):
		lowerCAmelCase  :     List[str]       						= len([g for position, g in enumerate(a_						) if g == main_target[position]]						)
		return (item, float(a_						))






def   __A    (				a_    :	str   ,a_    :	str						):
		lowerCAmelCase  :     Tuple       						= random.randint(0   ,len(a_						) - 1						)
		lowerCAmelCase  :     Optional[Any]       						= parent_a[:random_slice] + parent_a[random_slice:]
		lowerCAmelCase  :     Optional[Any]       						= parent_a[:random_slice] + parent_a[random_slice:]
		return (child_a, child_a)






def   __A    (				a_    :	str   ,a_    :	list[str]						):
		lowerCAmelCase  :     Dict       						= list(a_						)
		if random.uniform(0   ,1						) < MUTATION_PROBABILITY:
				lowerCAmelCase  :     List[Any]       						= random.choice(a_						)
		return "".join(a_						)






def   __A    (				a_    :	tuple[str, float]   ,a_    :	list[tuple[str, float]]   ,a_    :	list[str]   ,):
		lowerCAmelCase  :     Union[str, Any]       						= []
		# Generate more children proportionally to the fitness score.
		lowerCAmelCase  :     List[Any]       						= int(parent_a[1] * 1_0_0						) + 1
		lowerCAmelCase  :     str       						= 1_0 if child_n >= 1_0 else child_n
		for _ in range(a_						):
				lowerCAmelCase  :     Dict       						= population_score[random.randint(0   ,a_						)][0]

				lowerCAmelCase						,		lowerCAmelCase  :     Dict       						= crossover(parent_a[0]   ,a_						)
				# Append new string to the population list.
				pop.append(mutate(a_   ,a_						)						)
				pop.append(mutate(a_   ,a_						)						)
		return pop






def   __A    (				a_    :	str   ,a_    :	list[str]   ,a_    :	bool = True						):

		# Verify if N_POPULATION is bigger than N_SELECTED
		if N_POPULATION < N_SELECTED:
				lowerCAmelCase  :     List[Any]       						= f'''{N_POPULATION} must be bigger than {N_SELECTED}'''
				raise ValueError(a_						)
		# Verify that the target contains no genes besides the ones inside genes variable.
		lowerCAmelCase  :     Any       						= sorted({c for c in target if c not in genes}						)
		if not_in_genes_list:
				lowerCAmelCase  :     Optional[int]       						= f'''{not_in_genes_list} is not in genes list, evolution cannot converge'''
				raise ValueError(a_						)

		# Generate random starting population.
		lowerCAmelCase  :     int       						= []
		for _ in range(a_						):
				population.append("".join([random.choice(a_						) for i in range(len(a_						)						)]						)						)

		# Just some logs to know what the algorithms is doing.
		lowerCAmelCase						,		lowerCAmelCase  :     int       						= 0, 0

		# This loop will end when we find a perfect match for our target.
		while True:
				generation += 1
				total_population += len(a_						)

				# Random population created. Now it's time to evaluate.

				# Adding a bit of concurrency can make everything faster,
				#
				# import concurrent.futures
				# population_score: list[tuple[str, float]] = []
				# with concurrent.futures.ThreadPoolExecutor(
				#                                   max_workers=NUM_WORKERS) as executor:
				#     futures = {executor.submit(evaluate, item) for item in population}
				#     concurrent.futures.wait(futures)
				#     population_score = [item.result() for item in futures]
				#
				# but with a simple algorithm like this, it will probably be slower.
				# We just need to call evaluate for every item inside the population.
				lowerCAmelCase  :     Optional[int]       						= [evaluate(a_   ,a_						) for item in population]

				# Check if there is a matching evolution.
				lowerCAmelCase  :     str       						= sorted(a_   ,key=lambda a_						: x[1]   ,reverse=a_						)
				if population_score[0][0] == target:
						return (generation, total_population, population_score[0][0])

				# Print the best result every 10 generation.
				# Just to know that the algorithm is working.
				if debug and generation % 1_0 == 0:
						print(
						    f'''\nGeneration: {generation}'''
						    f'''\nTotal Population:{total_population}'''
						    f'''\nBest score: {population_score[0][1]}'''
						    f'''\nBest string: {population_score[0][0]}'''						)

				# Flush the old population, keeping some of the best evolutions.
				# Keeping this avoid regression of evolution.
				lowerCAmelCase  :     Any       						= population[: int(N_POPULATION / 3						)]
				population.clear()
				population.extend(a_						)
				# Normalize population score to be between 0 and 1.
				lowerCAmelCase  :     Union[str, Any]       						= [
				    (item, score / len(a_						)) for item, score in population_score
				]

				# This is selection
				for i in range(a_						):
						population.extend(select(population_score[int(a_						)]   ,a_   ,a_						)						)
						# Check if the population has already reached the maximum value and if so,
						# break the cycle.  If this check is disabled, the algorithm will take
						# forever to compute large strings, but will also calculate small strings in
						# a far fewer generations.
						if len(a_						) > N_POPULATION:
								break


if __name__ == "__main__":
					lowerCAmelCase									=   (
					    """This is a genetic algorithm to evaluate, combine, evolve, and mutate a string!"""
					)
					lowerCAmelCase									=   list(
					    """ ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklm"""
					    """nopqrstuvwxyz.,;!?+-*#@^'èéòà€ù=)(&%$£/\\"""
					)
					lowerCAmelCase		,    lowerCAmelCase		,    lowerCAmelCase									=   basic(target_str, genes_list)
					print(
					    F'''\nGeneration: {generation}\nTotal Population: {population}\nTarget: {target}'''
					)










'''simple docstring'''






lowerCAmelCase									=   [sum(int(c, 10) ** 2 for c in i.__str__()) for i in range(10_00_00)]






def   __A    (				a_    :	int						):
		lowerCAmelCase  :     Optional[int]       						= 0
		while number:
				# Increased Speed Slightly by checking every 5 digits together.
				sum_of_digits_squared += DIGITS_SQUARED[number % 1_0_0_0_0_0]
				number //= 1_0_0_0_0_0

		return sum_of_digits_squared


# There are 2 Chains made,
# One ends with 89 with the chain member 58 being the one which when declared first,
# there will be the least number of iterations for all the members to be checked.

# The other one ends with 1 and has only one element 1.

# So 58 and 1 are chosen to be declared at the starting.

# Changed dictionary to an array to quicken the solution
lowerCAmelCase									=   [None] * 10_00_00_00
lowerCAmelCase									=   True
lowerCAmelCase									=   False






def   __A    (				a_    :	int						):

		if CHAINS[number - 1] is not None:
				return CHAINS[number - 1]  # type: ignore

		lowerCAmelCase  :     Dict       						= chain(next_number(a_						)						)
		lowerCAmelCase  :     Union[str, Any]       						= number_chain

		while number < 1_0_0_0_0_0_0_0:
				lowerCAmelCase  :     Any       						= number_chain
				number *= 1_0

		return number_chain






def   __A    (				a_    :	int = 1_0_0_0_0_0_0_0						):
		for i in range(1   ,a_						):
				if CHAINS[i] is None:
						chain(i + 1						)

		return CHAINS[:number].count(a_						)


if __name__ == "__main__":
					import doctest

					doctest.testmod()
					print(F'''{solution() = }''')



