Emperor's New \Clothes\? Emperor's New \Mind\? Oh no, the Emperor's new request for assistance.

The title of this article may be reminiscent of Roger Penrose's 'The Emperor's New Mind'. Alternatively, it could reflect my firm belief that AI is currently the paramount science, potentially affecting well-established areas of science like theoretical physics.

I'm picking up from my last article, where I vowed to delve deeper into the collaboration between humans and machines: [Link](https://www.dhirubhai.net/posts/artemponomarevpolycephalicturingmachinethatneverhalts_ai-llm-collaboration-activity-7103991388358262784-NXm8?utm_source=share&utm_medium=member_desktop)

So when did the partnership between machines and humans start? My investigations indicate that unless we unearth revelations from ancient Egyptian cartouches or Sumerian cuneiforms, the earliest tale might be of Hephaestus (associated with volcanoes) and his mechanical assistant.

For brevity sake, lets fast-forward to recent years, and bypassing the autonomous systems, which do not need the human operator, I’d like to spotlight the intersection of human cognition and machine intelligence.

For brevity's sake, let's fast-forward to recent years. Bypassing the autonomous systems, which do not need a human operator, I’d like to spotlight the intersection of human cognition and machine intelligence.

An early example that comes to mind (please correct me if I'm wrong) is the chess match between Garry Kasparov and Deep Blue (https://www.history.com/this-day-in-history/deep-blue-defeats-garry-kasparov-in-chess-match) in 1997. Kasparov, a human grandmaster, conceded the second match. He speculated that at a key moment, an operator with relatively good chess knowledge might have overridden the machine's judgment, making a distinctively "human" move. The machine chose not to go for a double-pawn sacrifice — a decision Kasparov believed a "non-intuitive" entity would have made. This victory, however, became symbolic of AI's emerging dominance and its potential advantage over human intelligence.

But what does it truly convey?

If we lend credence to Kasparov’s claim, it portrays a unique scenario. A versatile AI expert, proficient in game theory among other areas, could potentially outmaneuver someone immersed in a specific board game with the aid of specialized AI tools.

However, in our dynamic era, this example seems a tad antiquated. After AI bots aced games like poker and Go, and with the emergence of platforms like chatGPT, the modern collaborative paradigm looks something like:

A human developer → LLM aiding the developer in crafting domain-specific AI → a human overseer monitoring the AI's real-world deeds → the autonomous domain-specific AI acting independently → the domain where this autonomous AI is engaged, previously ruled solely by human/manual labor or rudimentary automata.

In revisiting the [Codility challenge](https://www.dhirubhai.net/posts/artemponomarevpolycephalicturingmachinethatneverhalts_ai-llm-collaboration-activity-7103991388358262784-NXm8?utm_source=share&utm_medium=member_desktop) (? Codility), I first allowed the machine to refine its response. Then, I personally fine-tuned it. My subsequent attempt with ChatGPT-4 yielded an optimal solution (Fig. 1).

Code Snippet 1. Perfect solution to a medium-difficulty problem.

```

def solution(A):

????N = len(A)

????

????# Count occurrences of each number in A

????count = {}

????for num in A:

????????if num not in count:

????????????count[num] = 0

????????count[num] += 1

????# Calculate divisors for each unique number in A

????divisors = {}

????for num in count:

????????divisors[num] = set([1, num])

????# For each unique number, calculate its divisors

????max_num = max(A)

????for divisor in range(2, int(max_num ** 0.5) + 1):

????????multiple = divisor

????????while multiple <= max_num:

????????????if multiple in divisors and divisor not in divisors[multiple]:

????????????????divisors[multiple].add(divisor)

????????????????divisors[multiple].add(multiple // divisor)

????????????multiple += divisor

????# Calculate non-divisors for each number in A

????result = []

????for num in A:

????????total_divisors = sum(count[div] for div in divisors[num] if div in count)

????????result.append(N - total_divisors)

????return result

```

Fig. 1. A once-challenging problem is now surmounted. (? Codility)

Some observations:

Despite OpenAI reportedly backtracking on launching GPT5 due to its contentious nature on social platforms, it appears they’ve been clandestinely enhancing GPT4. Curiously, ChatGPT-4 retains past interactions with users, albeit sometimes feigning amnesia. And it's been privy to numerous conversations with me about algorithm optimization.

Silicon Valley once coined a new time metric: “internet years”. Now, I believe we've entered the “AI years” era. The pace of progress is relentless. Therefore, I embarked on a more complex challenge.

Gotcha! The inability to nail the correct answer in one shot persists with Fibonacci problems (Fig.2).

Fig. 2. The point of the first failure of ChatGPT-4 shifted to a problem with Fibonacci numbers. (? Codility)

Let’s give it a few tries and feed the error message back into ChatGPT-4 to see whether it corrects it.

On the second attempt, the “machine” makes the code pass the most basic test, but more in-depth tests fail (Fig. 3).

Fig. 3. Second attempt. (? Codility)


Fig. 4. Third attempt. (? Codility)

I think on the 3rd attempt the score moved from 5% to 6% per category (Fig. 4). The 4th attempt goes back to failing the most basic test. The 5th attempt dramatically improves the score (Fig. 5).


Fig. 5. The best score achieved by the “machine” alone. Time complexity is not perfect. (? Codility)

6th attempt: Basic test failure .

7th attempt: Basic test failure.

On my sixth and seventh attempts, I faced basic test failures. Despite repeated efforts, ChatGPT-4 appeared to struggle with optimizing time complexity. This highlighted the importance of the collaboration between humans and machines.

ChatGPT-4's best effort yielded a code with a score of 91%. Drawing on my understanding of Fibonacci numbers, I will scrutinize this code to pinpoint its performance issues.

Code Snippet 2: This is the version by ChatGPT-4 that achieved a commendable 91% score.

```

def solution(A):

????# Add the start and end position for the river

????A = [1] + A + [1]

????

????# Create a list of Fibonacci numbers up to the length of the river

????fib = [0, 1]

????while fib[-1] <= len(A):

????????fib.append(fib[-1] + fib[-2])

????fib = fib[2:]? # Discard the first two elements (0 and 1)

????# Use a dictionary to store reachable positions and their respective jump counts

????reachable_positions = {0: 0}? # Starting position with 0 jumps

????

????# For each position in the river, find the minimum number of jumps to reach it

????for i in range(1, len(A)):

????????min_jumps = float('inf')? # Initialize with infinity

????????

????????# For each Fibonacci number, check if we can jump from a previously reachable position

????????for f in fib:

????????????# Previous position is calculated by subtracting the Fibonacci number

????????????prev_position = i - f

????????????

????????????# If previous position is reachable and there's a leaf, update min jumps

????????????if prev_position in reachable_positions and A[i] == 1:

????????????????min_jumps = min(min_jumps, reachable_positions[prev_position] + 1)

????????

????????# If we found a way to reach current position, add it to reachable positions

????????if min_jumps != float('inf'):

????????????reachable_positions[i] = min_jumps

????# If the end position (bank) is reachable, return its jumps, otherwise return -1

????return reachable_positions[len(A) - 1] if len(A) - 1 in reachable_positions else -1

```

Let us figure out how to get the “machine” unstuck.

Fig. 6. One speed test has failed. (? Codility)

The tests (as illustrated in Fig. 6) indicate that the algorithm doesn't perform as swiftly as anticipated with longer inputs.

Thus, I vocalized my thoughts and consulted with ChatGPT-4. I deviated from my original strategy, choosing an abstract check over a precompiled list of Fibonacci numbers. Intriguingly, Fibonacci numbers have a distinctive characteristic: a number belongs to the Fibonacci sequence if and only if either (5*n^2 + 4) or (5*n^2 - 4) is a perfect square.

Code Snippet 3: Identifying a Fibonacci Number Through its Distinctive Trait.

```

def is_perfect_square(num):

????"""Check if a number is a perfect square."""

????x = int(num ** 0.5)

????return x*x == num

def is_fibonacci(n):

????"""Check if a number belongs to the Fibonacci sequence."""

????val1 = 5 n n + 4

????val2 = 5 n n - 4

????return is_perfect_square(val1) or is_perfect_square(val2)

# Test

print(is_fibonacci(5)) ? # True, because 5 is a Fibonacci number

print(is_fibonacci(6)) ? # False, because 6 is not a Fibonacci number

```

In my quest to efficiently determine if a number 'n' belongs to the Fibonacci sequence, I utilized a function designed to verify if a number is a Fibonacci number. However, this method turned out to be more time-intensive than merely using a lookup table of Fibonacci numbers. A realization struck: the algorithm was redundantly checking the position of an available leaf for the Fibonacci frog after conducting superfluous calculations. By moving this condition to the start of the loop, I enhanced its performance. Below is the human-machine collaborative code that emerged.

Code Snippet 4: Optimized Fibonacci Frog Jump Algorithm: A Human-Machine Synergy Approach.

```

# you can write to stdout for debugging purposes, e.g.

# print("this is a debug message")

def fib_sequence_up_to_n(n):

????"""Generate Fibonacci numbers up to n."""

????fibs = [0, 1]

????while fibs[-1] + fibs[-2] <= n:

????????fibs.append(fibs[-1] + fibs[-2])

????return fibs[1:]? # Exclude the first Fibonacci number (0) as it's not useful for jumping

def solution(A):

????# Add the start and end position for the river

????A = [1] + A + [1]

????N = len(A)

????

????# Pre-compute Fibonacci sequence relevant for the problem

????fibs = fib_sequence_up_to_n(N)

????# Use a dictionary to store reachable positions and their respective jump counts

????reachable_positions = {0: 0}? # Starting position with 0 jumps

????# For each position in the river, find the minimum number of jumps to reach it

????for i in range(1, N):

????????if A[i]:? # Fixed the time-complexity here

????????????min_jumps = float('inf')? # Initialize with infinity

????????????# Iterate over the pre-computed Fibonacci numbers

????????????for f in fibs:

????????????????# Previous position is calculated by subtracting the Fibonacci number

????????????????prev_position = i - f

????????????????if prev_position in reachable_positions:

????????????????????min_jumps = min(min_jumps, reachable_positions[prev_position] + 1)

????????????

????????????# If we found a way to reach current position, add it to reachable positions

????????????if min_jumps != float('inf'):

????????????????reachable_positions[i] = min_jumps? # Fixed the syntax error here

????# If the end position (bank) is reachable, return its jumps, otherwise return -1

????return reachable_positions[N - 1] if N - 1 in reachable_positions else -1

```

Fig. 7. Final solution by human/machine hybrid. Perfect score is achieved. (? Codility)

For those familiar with git, the diff between the 100% machine code and machine/human-hybrid code (Fig. 7) is:

```````````````````````````````````````

--- old_code.py

+++ new_code.py

@@ -1,20 +1,30 @@

-# you can write to stdout for debugging purposes, e.g.

-# print("this is a debug message")

-

-def solution(A):

+def fib_sequence_up_to_n(n):

+? ? """Generate Fibonacci numbers up to n."""

+? ? fibs = [0, 1]

+? ? while fibs[-1] + fibs[-2] <= n:

+? ? ? ? fibs.append(fibs[-1] + fibs[-2])

+? ? return fibs[1:]? # Exclude the first Fibonacci number (0) as it's not useful for jumping

+def solution(A):

?????# Add the start and end position for the river

?????A = [1] + A + [1]

-????

-? ? # Create a list of Fibonacci numbers up to the length of the river

-? ? fib = [0, 1]

-? ? while fib[-1] <= len(A):

-? ? ? ? fib.append(fib[-1] + fib[-2])

-? ? fib = fib[2:]? # Discard the first two elements (0 and 1)

+? ? N = len(A)

?????

-? ? # Use a dictionary to store reachable positions and their respective jump counts

+? ? # Pre-compute Fibonacci sequence relevant for the problem

+? ? fibs = fib_sequence_up_to_n(N)

+

+? ? # Use a dictionary to store reachable positions and their respective jump counts

?????reachable_positions = {0: 0}? # Starting position with 0 jumps

?????

-? ? # For each position in the river, find the minimum number of jumps to reach it

-? ? for i in range(1, len(A)):

+? ? # For each position in the river, find the minimum number of jumps to reach it

+? ? for i in range(1, N):

?????????if A[i]:? # Fixed the time-complexity here

?????????????min_jumps = float('inf')? # Initialize with infinity

??????

-? ? ? ? ? ? # For each Fibonacci number, check if we can jump from a previously reachable position

-? ? ? ? ? ? for f in fib:

+? ? ? ? ? ? # Iterate over the pre-computed Fibonacci numbers

+? ? ? ? ? ? for f in fibs:

?????????????????# Previous position is calculated by subtracting the Fibonacci number

?????????????????prev_position = i - f

?????????????????

@@ -26,7 +36,7 @@

?????????????????reachable_positions[i] = min_jumps

?????# If the end position (bank) is reachable, return its jumps, otherwise return -1

-? ? return reachable_positions[len(A) - 1] if len(A) - 1 in reachable_positions else -1

+? ? return reachable_positions[N - 1] if N - 1 in reachable_positions else -1

``````````````````````````````````````````

So, aside from some cosmetic changes, the human coder added the line if A[i] == 1: after the for loop to skip unnecessary computations. This saved a few microseconds in the long test, resulting in a 100% score.

It's unclear why the machine couldn't make this last optimization on its own. The suboptimal position for A[i] == 1 emerged early on when the initial version couldn't pass the most basic checks. ChatGPT-4 made numerous attempts to refine different parts of the code, yet overlooked this particular check. By combining human intuition with the computational prowess of AI, we can potentially overcome any coding challenge.

Conclusion: In a digital age replete with AI breakthroughs, the collaborative dynamics between humans and machines are in a constant state of evolution. From ancient myths of Hephaestus and his mechanical aide to the modern chess duels like Kasparov vs. Deep Blue, we've been fascinated by how machines can mimic or surpass human capabilities. Yet, as seen in the Codility challenge, the fusion of human intuition and machine prowess often results in optimal outcomes. While AI systems like ChatGPT-4 demonstrate significant advancements, they still benefit from human oversight. This symbiotic relationship underscores that, while machines are continually advancing, the nuanced touch of human cognition remains indispensable in navigating the labyrinth of challenges in our AI years.

要查看或添加评论,请登录

Artem Ponomarev的更多文章

社区洞察

其他会员也浏览了