2026京东笔试真题【风控排序评估】多语言题解

风控排序评估(C++/Py/Java /Js/Go)题解

京东 2026年9月19号 笔试真题 第一题

题目内容

风控侧要对一批交易做欺诈打分。每笔交易有真实标签 tit_iti(000 表示正常,111 表示欺诈)以及模型给出的风险分 sis_isi(越大越像欺诈)。

请按 Mann-Whitney U 的秩统计来评估这批打分的排序质量,也就是 AUC。分数相同的交易必须用平均名次,不能随便拆开。

规则如下:

  1. 平均名次:把 mmm 笔交易按 sss 从小到大排。名次从 111 起。若连续若干笔分数相同,占住第 LLL 到第 RRR 个位置,则它们的名次都取 (L+R)/2(L+R)/2(L+R)/2。
  2. 由正类名次和还原 AUC:记欺诈笔数为 kkk,正常笔数为 ggg,欺诈样本的名次之和为 SSS。令 V=S−k(k+1)/2V = S - k(k+1)/2V=S−k(k+1)/2,AUC=V/(k⋅g)AUC = V/(k\cdot g)AUC=V/(k⋅g)。

输入描述

第一行一个整数 mmm,表示交易笔数。(2≤m≤19)(2 \le m \le 19)(2≤m≤19)

第二行 mmm 个整数 tit_iti,即标签,元素只能是 000 或 111。保证两种标签都出现。

第三行 mmm 个浮点数 sis_isi,即风险分。

输出描述

输出一行一个浮点数,表示 AUC,必须恰好保留 666 位小数。

样例1

输入

复制代码
4
0 0 0 1
0.1 0.2 0.9 0.5

输出

复制代码
0.666667

说明

  • 按分数升序:(0.1,0)(0.1,0)(0.1,0) 名次 111,(0.2,0)(0.2,0)(0.2,0) 名次 222,(0.5,1)(0.5,1)(0.5,1) 名次 333,(0.9,0)(0.9,0)(0.9,0) 名次 444。
  • 欺诈只有一笔,名次 S=3S=3S=3,k=1k=1k=1,g=3g=3g=3。
  • V=3−1=2V=3-1=2V=3−1=2,AUC=2/3=0.666667AUC=2/3=0.666667AUC=2/3=0.666667。

样例2

输入

复制代码
3
0 1 1
0.5 0.5 0.8

输出

复制代码
0.750000

说明

  • 两个 0.50.50.5 并列占第 111、222 名,平均名次都是 1.51.51.5;分数 0.80.80.8 的名次是 333。
  • 欺诈名次和 S=1.5+3=4.5S=1.5+3=4.5S=1.5+3=4.5,k=2k=2k=2,g=1g=1g=1。
  • V=4.5−3=1.5V=4.5-3=1.5V=4.5−3=1.5,AUC=1.5/2=0.750000AUC=1.5/2=0.750000AUC=1.5/2=0.750000。

题解和思路

思路

实现思路:模拟、排序

  1. 按照s从小到大进行排序,然后计算欺诈样本的平均名词之和。
  2. 将每笔交易保存为(score,label)
  3. 按照socre进行从小到大升序排序
  4. 统计欺诈样本和正常样本个数k, g
  5. 然后从前往后进行处理,每次处理相同分数排名片段,假设占据L-R名,则对应名词为rank = (L + R) /2, 如果其中包含欺诈样本,则对应S + rank。
  6. 最后按照公式计算对应结果即可。
  7. 时间复杂度为O(m)

C++

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

struct Node {
    double score;
    int label;
};

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int m;
    cin >> m;

    vector<int> t(m);
    for (int i = 0; i < m; i++) {
        cin >> t[i];
    }

    vector<Node> a(m);
    for (int i = 0; i < m; i++) {
        cin >> a[i].score;
        a[i].label = t[i];
    }

    sort(a.begin(), a.end(), [](const Node& x, const Node& y) {
        return x.score < y.score;
    });

    int k = 0, g = 0;
    for (auto& x : a) {
        if (x.label == 1) k++;
        else g++;
    }

    // S:欺诈样本的平均名次之和
    double S = 0;

    int i = 0;
    while (i < m) {
        int j = i;

        // 找到分数相同的一组
        while (j + 1 < m && a[j + 1].score == a[i].score) {
            j++;
        }

        // 这一组占据第 i+1 到 j+1 名
        double rank = ((i + 1) + (j + 1)) / 2.0;

        // 相同分数的所有样本使用相同的平均名次
        for (int p = i; p <= j; p++) {
            if (a[p].label == 1) {
                S += rank;
            }
        }

        i = j + 1;
    }

    double V = S - 1.0 * k * (k + 1) / 2;
    double auc = V / (1.0 * k * g);

    cout << fixed << setprecision(6) << auc << '\n';

    return 0;
}

Java

java 复制代码
import java.util.*;

public class Main {

    static class Node {
        double score;
        int label;

        Node(double score, int label) {
            this.score = score;
            this.label = label;
        }
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        int m = sc.nextInt();

        int[] t = new int[m];
        for (int i = 0; i < m; i++) {
            t[i] = sc.nextInt();
        }

        Node[] a = new Node[m];
        for (int i = 0; i < m; i++) {
            double score = sc.nextDouble();
            a[i] = new Node(score, t[i]);
        }

        Arrays.sort(a, (x, y) -> Double.compare(x.score, y.score));

        int k = 0, g = 0;
        for (Node x : a) {
            if (x.label == 1) {
                k++;
            } else {
                g++;
            }
        }

        // S:欺诈样本的平均名次之和
        double S = 0;

        int i = 0;
        while (i < m) {
            int j = i;

            // 找到分数相同的一组
            while (j + 1 < m && a[j + 1].score == a[i].score) {
                j++;
            }

            // 这一组占据第 i+1 到 j+1 名
            double rank = ((i + 1) + (j + 1)) / 2.0;

            // 相同分数的所有样本使用相同的平均名次
            for (int p = i; p <= j; p++) {
                if (a[p].label == 1) {
                    S += rank;
                }
            }

            i = j + 1;
        }

        double V = S - 1.0 * k * (k + 1) / 2;
        double auc = V / (1.0 * k * g);

        System.out.printf("%.6f%n", auc);
    }
}

python

python 复制代码
import sys

class Node:
    def __init__(self, score, label):
        self.score = score
        self.label = label


def main():
    data = sys.stdin.read().split()
    pos = 0

    m = int(data[pos])
    pos += 1

    t = []
    for i in range(m):
        t.append(int(data[pos]))
        pos += 1

    a = []
    for i in range(m):
        score = float(data[pos])
        pos += 1
        a.append(Node(score, t[i]))

    a.sort(key=lambda x: x.score)

    k = 0
    g = 0
    for x in a:
        if x.label == 1:
            k += 1
        else:
            g += 1

    # S:欺诈样本的平均名次之和
    S = 0.0

    i = 0
    while i < m:
        j = i

        # 找到分数相同的一组
        while j + 1 < m and a[j + 1].score == a[i].score:
            j += 1

        # 这一组占据第 i+1 到 j+1 名
        rank = ((i + 1) + (j + 1)) / 2.0

        # 相同分数的所有样本使用相同的平均名次
        for p in range(i, j + 1):
            if a[p].label == 1:
                S += rank

        i = j + 1

    V = S - 1.0 * k * (k + 1) / 2
    auc = V / (1.0 * k * g)

    print(f"{auc:.6f}")


if __name__ == "__main__":
    main()

Javascript

js 复制代码
const readline = require('readline');

const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
});

const input = [];

rl.on('line', line => {
    input.push(...line.trim().split(/\s+/));
});

rl.on('close', () => {
    let pos = 0;

    const m = Number(input[pos++]);

    const t = [];
    for (let i = 0; i < m; i++) {
        t.push(Number(input[pos++]));
    }

    const a = [];
    for (let i = 0; i < m; i++) {
        const score = Number(input[pos++]);
        a.push({
            score: score,
            label: t[i]
        });
    }

    a.sort((x, y) => x.score - y.score);

    let k = 0;
    let g = 0;

    for (const x of a) {
        if (x.label === 1) {
            k++;
        } else {
            g++;
        }
    }

    // S:欺诈样本的平均名次之和
    let S = 0;

    let i = 0;
    while (i < m) {
        let j = i;

        // 找到分数相同的一组
        while (j + 1 < m && a[j + 1].score === a[i].score) {
            j++;
        }

        // 这一组占据第 i+1 到 j+1 名
        const rank = ((i + 1) + (j + 1)) / 2.0;

        // 相同分数的所有样本使用相同的平均名次
        for (let p = i; p <= j; p++) {
            if (a[p].label === 1) {
                S += rank;
            }
        }

        i = j + 1;
    }

    const V = S - 1.0 * k * (k + 1) / 2;
    const auc = V / (1.0 * k * g);

    console.log(auc.toFixed(6));
});

Go

go 复制代码
package main

import (
	"bufio"
	"fmt"
	"os"
	"sort"
)

type Node struct {
	score float64
	label int
}

func main() {
	in := bufio.NewReader(os.Stdin)
	out := bufio.NewWriter(os.Stdout)
	defer out.Flush()

	var m int
	fmt.Fscan(in, &m)

	t := make([]int, m)
	for i := 0; i < m; i++ {
		fmt.Fscan(in, &t[i])
	}

	a := make([]Node, m)
	for i := 0; i < m; i++ {
		fmt.Fscan(in, &a[i].score)
		a[i].label = t[i]
	}

	sort.Slice(a, func(i, j int) bool {
		return a[i].score < a[j].score
	})

	k, g := 0, 0
	for _, x := range a {
		if x.label == 1 {
			k++
		} else {
			g++
		}
	}

	// S:欺诈样本的平均名次之和
	S := 0.0

	i := 0
	for i < m {
		j := i

		// 找到分数相同的一组
		for j+1 < m && a[j+1].score == a[i].score {
			j++
		}

		// 这一组占据第 i+1 到 j+1 名
		rank := float64((i+1)+(j+1)) / 2.0

		// 相同分数的所有样本使用相同的平均名次
		for p := i; p <= j; p++ {
			if a[p].label == 1 {
				S += rank
			}
		}

		i = j + 1
	}

	V := S - 1.0*float64(k*(k+1))/2
	auc := V / (1.0 * float64(k*g))

	fmt.Fprintf(out, "%.6f\n", auc)
}
相关推荐
无限码力3 天前
京东笔试真题 - 过道对岗(C++/Py/Java /Js/Go)题解
京东·京东笔试真题·京东笔试·京东笔试真题题解·京东最新笔试题解
无限码力17 天前
2026京东笔试真题【第几条明暗交错子序列】多语言题解
京东·京东笔试真题·京东笔试·京东笔试真题题解·京东提前批笔试真题·京东笔试题解
无限码力1 个月前
京东笔试真题 - 以弱胜强的秘密(C++/Py/Java /Js/Go)
京东·京东笔试真题·京东机试·京东最新笔试真题·京东技术岗笔试·京东最新笔试真题题解
无限码力1 个月前
京东笔试真题 - 失落的宝石(C++/Py/Java /Js/Go)
京东·京东笔试真题·京东技术岗笔试真题·京东笔试·京东机试